[
  {
    "path": ".classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.nar\n*.ear\n*.zip\n*.tar.gz\n*.rar\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n"
  },
  {
    "path": ".project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": ".settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding/<project>=UTF-8\n"
  },
  {
    "path": ".settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": ".settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# ![Leo-IM](https://raw.githubusercontent.com/wiki/lining90567/leo-im-server/leo-im.png)\n\nLeo-IM，开源好用的IM。\n\nLeo-IM是基于Java语言、Netty框架、Vue+Element-UI开发的轻量级IM，服务端可独立运行（无需部署到web容器），HTTP服务和Socket服务可分开部署，使用基于Netty扩展的[netty-rest-server](https://github.com/lining90567/netty-rest-server)RESTful框架提供Web服务，简单易用，方便扩展。\n\n## 在线演示\n\n演示地址：<a href=\"http://43.138.44.47:8000\" target=\"_blank\">http://43.138.44.47:8000</a>\n\n建议使用Chrome浏览器\n\n- 演示用户1：用户名 test1，口令 123456\n- 演示用户2：用户名 test2，口令 123456\n- 演示用户3：用户名 test3，口令 123456\n\n## 运行环境要求\n\n- 服务端：Java8、MySQL5.5+\n- 客户端：Chrome、IE10+\n\n## 主要功能\n\n- 私聊\n- 群聊\n- 文字、表情、图片、文件\n\n## 构建与部署\n\n- 安装netty-rest-server到本地仓库\n\n\tmvn install:install-file -Dfile=netty-rest-server-1.0.jar -DgroupId=org.leo -DartifactId=netty-rest-server -Dversion=1.0 -Dpackaging=jar\n\n- 创建数据库，并设置字符集（my.cnf或my.ini）\n\n\t[client]\n\t\n\tdefault-character-set=utf8mb4\n\n\t[mysqld]\n\t\n\tcharacter-set-client-handshake = FALSE\n\n\tcharacter-set-server = utf8mb4\n\n\tcollation-server = utf8mb4_unicode_ci\n\n\tinit_connect=’SET NAMES utf8mb4'\n\n\t[mysql]\n\t\n\tdefault-character-set=utf8mb4\n\n- 构建\n\n\tmvn package\n\n- 部署\n\n\t解压leo-im-1.0.zip，修改conf/app.conf的相关配置\n\n- 启动\n\n\tnohup bin/run.sh >/dev/null 2>&1 &\n\t\n- Web端代码\n\t\n\t<a href=\"http://43.138.44.47:8000\" target=\"_blank\">https://github.com/lining90567/leo-im-web</a>\n\t\n## 联系方式\n- **邮箱** - lining90567@sina.com\n- **QQ** - 328616209\n"
  },
  {
    "path": "assemble/bin/run.bat",
    "content": "@REM app launcher script\n@REM\n@REM Environment:\n@REM JAVA_HOME - location of a JDK home dir (optional if java on path)\n@setlocal enabledelayedexpansion\n\n@echo off\n\ncd %~dp0\ncd ../\n\nif \"%JAVA_OPS%\" == \"\" set JAVA_OPS=-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -server -Xmx128m -Xms128m -Xss256k\njava %JAVA_OPS% -Dconf.home=%cd%\\conf\\ -jar %cd%\\lib\\leo-im-starter-1.0.jar"
  },
  {
    "path": "assemble/bin/run.sh",
    "content": "#!/bin/sh\n\n###  ------------------------------- ###\n###  leo-im-server launcher script   ###\n###  ------------------------------- ###\n\ncd `dirname $0`\ncd ../\nif [ -z \"$JAVA_OPS\" ]; then\n  JAVA_OPS=\"-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -Xms128M -Xmx128M -Xss256K\"\nfi\njava $JAVA_OPS -Dconf.home=$(pwd)/conf/ -jar  $(pwd)/lib/leo-im-starter-1.0.jar"
  },
  {
    "path": "assemble/package.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/assembly-1.0.0.xsd\">\n    <id>package</id>\n    <formats>\n        <format>zip</format>\n    </formats>\n    <includeBaseDirectory>true</includeBaseDirectory>\n    <fileSets>  \n        <fileSet>\n            <directory>../assemble/bin</directory>\n            <outputDirectory>/bin</outputDirectory>\n        </fileSet>\n        <fileSet>\n            <directory>src/main/conf</directory>\n            <outputDirectory>/conf</outputDirectory>\n        </fileSet>\n    </fileSets>\n    <dependencySets>\n        <dependencySet>\n            <outputDirectory>lib</outputDirectory>\n            <scope>runtime</scope>      \n        </dependencySet>                \n    </dependencySets>\n</assembly>"
  },
  {
    "path": "leo-im-api/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-api/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-api</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-api/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-api/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-api/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-api/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-api</artifactId>\n\t<name>leo-im-api</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-common</artifactId>\n            <version>${parent.version}</version>\n        </dependency>       \n    </dependencies>\t\n</project>\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/annotation/Cacheable.java",
    "content": "package org.leo.im.api.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 缓存注解\n * \n * @author Leo\n * @date 2018/3/19\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\npublic @interface Cacheable {\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/annotation/Transactional.java",
    "content": "package org.leo.im.api.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\n\n/**\n * 事务注解，该注解需要在服务接口上使用，在实现类上使用无效。\n * \n * @author Leo\n * @date 2018/3/19\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\npublic @interface Transactional {\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/ChannelDTO.java",
    "content": "package org.leo.im.api.dto;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 频道dto类\n * \n * @author Leo\n * @date 2018/4/11\n */\npublic final class ChannelDTO {\n\n    private String id;\n\n    private String name;\n\n    private String type;\n\n    private int memberCount;\n    \n    private long createAt;\n    \n    private String creatorId;\n    \n    private String fromUserId;\n    \n    private String fromUsername;\n    \n    private String fromUserNickname;\n    \n    private String toUserId;\n    \n    private String toUsername;\n    \n    private String toUserNickname;\n    \n    private String toUserOnlineStatus;\n    \n    private String purpose;\n    \n    private List<ChannelMemberDTO> members = new ArrayList<>(128);\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public int getMemberCount() {\n        return memberCount;\n    }\n\n    public void setMemberCount(int memberCount) {\n        this.memberCount = memberCount;\n    }\n\n    public long getCreateAt() {\n        return createAt;\n    }\n\n    public void setCreateAt(long createAt) {\n        this.createAt = createAt;\n    }\n\n    public String getCreatorId() {\n        return creatorId;\n    }\n\n    public void setCreatorId(String creatorId) {\n        this.creatorId = creatorId;\n    }\n\n    public String getFromUserId() {\n        return fromUserId;\n    }\n\n    public void setFromUserId(String fromUserId) {\n        this.fromUserId = fromUserId;\n    }\n\n    public String getFromUsername() {\n        return fromUsername;\n    }\n\n    public void setFromUsername(String fromUsername) {\n        this.fromUsername = fromUsername;\n    }\n\n    public String getFromUserNickname() {\n        return fromUserNickname;\n    }\n\n    public void setFromUserNickname(String fromUserNickname) {\n        this.fromUserNickname = fromUserNickname;\n    }\n\n    public String getToUserId() {\n        return toUserId;\n    }\n\n    public void setToUserId(String toUserId) {\n        this.toUserId = toUserId;\n    }\n\n    public String getToUsername() {\n        return toUsername;\n    }\n\n    public void setToUsername(String toUsername) {\n        this.toUsername = toUsername;\n    }\n\n    public String getToUserNickname() {\n        return toUserNickname;\n    }\n\n    public void setToUserNickname(String toUserNickname) {\n        this.toUserNickname = toUserNickname;\n    }\n\n    public String getToUserOnlineStatus() {\n        return toUserOnlineStatus;\n    }\n\n    public void setToUserOnlineStatus(String toUserOnlineStatus) {\n        this.toUserOnlineStatus = toUserOnlineStatus;\n    }\n\n    public List<ChannelMemberDTO> getMembers() {\n        return members;\n    }\n\n    public String getPurpose() {\n        return purpose;\n    }\n\n    public void setPurpose(String purpose) {\n        this.purpose = purpose;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChannelDTO [id=\" + id + \", name=\" + name + \", type=\" + type + \", memberCount=\" + memberCount\n                + \", createAt=\" + createAt + \", creatorId=\" + creatorId + \", fromUserId=\" + fromUserId\n                + \", fromUsername=\" + fromUsername + \", fromUserNickname=\" + fromUserNickname + \", toUserId=\" + toUserId\n                + \", toUsername=\" + toUsername + \", toUserNickname=\" + toUserNickname + \", toUserOnlineStatus=\"\n                + toUserOnlineStatus + \", purpose=\" + purpose + \", members=\" + members + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/ChannelListDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * Channel列表DTO\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class ChannelListDTO {\n\n    private String id;\n\n    private String name;\n    \n    private String displayName;\n\n    private String otherSideOnlineStatus;\n\n    private String type;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    public String getOtherSideOnlineStatus() {\n        return otherSideOnlineStatus;\n    }\n\n    public void setOtherSideOnlineStatus(String otherSideOnlineStatus) {\n        this.otherSideOnlineStatus = otherSideOnlineStatus;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChannelListDTO [id=\" + id + \", name=\" + name + \", displayName=\" + displayName\n                + \", otherSideOnlineStatus=\" + otherSideOnlineStatus + \", type=\" + type + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/ChannelMemberDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * 频道成员dto类\n * \n * @author Leo\n * @date 2018/4/11\n */\npublic class ChannelMemberDTO {\n    \n    private String id;\n    \n    private String nickname;\n    \n    private boolean admin;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public void setNickname(String nickname) {\n        this.nickname = nickname;\n    }\n\n    public boolean getAdmin() {\n        return admin;\n    }\n\n    public void setAdmin(boolean admin) {\n        this.admin = admin;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChannelMemberDTO [id=\" + id + \", nickname=\" + nickname + \", admin=\" + admin + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/FileDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * 文件dto类\n * \n * @author Leo\n * @date 2018/6/13\n */\npublic class FileDTO {\n    \n    private String id;\n    \n    private String name;\n    \n    private String extension;\n    \n    private int size;\n    \n    private String mimeType;\n    \n    private int width;\n    \n    private int height;\n    \n    private short thumbWidth;\n    \n    private short thumbHeight;\n    \n    private String path;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getExtension() {\n        return extension;\n    }\n\n    public void setExtension(String extension) {\n        this.extension = extension;\n    }\n\n    public int getSize() {\n        return size;\n    }\n\n    public void setSize(int size) {\n        this.size = size;\n    }\n\n    public String getMimeType() {\n        return mimeType;\n    }\n\n    public void setMimeType(String mimeType) {\n        this.mimeType = mimeType;\n    }\n\n    public int getWidth() {\n        return width;\n    }\n\n    public void setWidth(int width) {\n        this.width = width;\n    }\n\n    public int getHeight() {\n        return height;\n    }\n\n    public void setHeight(int height) {\n        this.height = height;\n    }\n\n    public short getThumbWidth() {\n        return thumbWidth;\n    }\n\n    public void setThumbWidth(short thumbWidth) {\n        this.thumbWidth = thumbWidth;\n    }\n\n    public short getThumbHeight() {\n        return thumbHeight;\n    }\n\n    public void setThumbHeight(short thumbHeight) {\n        this.thumbHeight = thumbHeight;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    @Override\n    public String toString() {\n        return \"FileDTO [id=\" + id + \", name=\" + name + \", extension=\" + extension + \", size=\" + size + \", mimeType=\"\n                + mimeType + \", width=\" + width + \", height=\" + height + \", thumbWidth=\" + thumbWidth + \", thumbHeight=\"\n                + thumbHeight + \", path=\" + path + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/MessageDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * 消息dto类\n * \n * @author Leo\n * @date 2018/5/15\n */\npublic class MessageDTO {\n\n    private long id;\n    \n    private String channelId;\n    \n    private String channelType;\n\n    private long createAt;\n\n    private String type;\n\n    private String senderId;\n\n    private String senderName;\n\n    private String senderNickname;\n\n    private String senderOnlineStatus;\n\n    private String senderAvatarUrl;\n    \n    private String senderFirstLetterOfName;\n    \n    private String content;\n    \n    private String fileId;\n    \n    private String fileName;\n    \n    private String fileExtension;\n    \n    private int fileSize;\n    \n    private String fileMimeType;\n    \n    private int imageWidth;\n    \n    private int imageHeight;\n    \n    private short imageThumbWidth;\n    \n    private short imageThumbHeight;    \n    \n    private String filePath;\n    \n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(String channelId) {\n        this.channelId = channelId;\n    }\n\n    public String getChannelType() {\n        return channelType;\n    }\n\n    public void setChannelType(String channelType) {\n        this.channelType = channelType;\n    }\n\n    public long getCreateAt() {\n        return createAt;\n    }\n\n    public void setCreateAt(long createAt) {\n        this.createAt = createAt;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getSenderId() {\n        return senderId;\n    }\n\n    public void setSenderId(String senderId) {\n        this.senderId = senderId;\n    }\n\n    public String getSenderName() {\n        return senderName;\n    }\n\n    public void setSenderName(String senderName) {\n        this.senderName = senderName;\n    }\n\n    public String getSenderNickname() {\n        return senderNickname;\n    }\n\n    public void setSenderNickname(String senderNickname) {\n        this.senderNickname = senderNickname;\n    }\n\n    public String getSenderOnlineStatus() {\n        return senderOnlineStatus;\n    }\n\n    public void setSenderOnlineStatus(String senderOnlineStatus) {\n        this.senderOnlineStatus = senderOnlineStatus;\n    }\n\n    public String getSenderAvatarUrl() {\n        return senderAvatarUrl;\n    }\n\n    public void setSenderAvatarUrl(String senderAvatarUrl) {\n        this.senderAvatarUrl = senderAvatarUrl;\n    }\n\n    public String getSenderFirstLetterOfName() {\n        return senderFirstLetterOfName;\n    }\n\n    public void setSenderFirstLetterOfName(String senderFirstLetterOfName) {\n        this.senderFirstLetterOfName = senderFirstLetterOfName;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(String content) {\n        this.content = content;\n    }\n\n    public String getFileMimeType() {\n        return fileMimeType;\n    }\n\n    public void setFileMimeType(String fileMimeType) {\n        this.fileMimeType = fileMimeType;\n    }\n\n    public int getImageWidth() {\n        return imageWidth;\n    }\n\n    public void setImageWidth(int imageWidth) {\n        this.imageWidth = imageWidth;\n    }\n\n    public int getImageHeight() {\n        return imageHeight;\n    }\n\n    public void setImageHeight(int imageHeight) {\n        this.imageHeight = imageHeight;\n    }\n\n    public short getImageThumbWidth() {\n        return imageThumbWidth;\n    }\n\n    public void setImageThumbWidth(short imageThumbWidth) {\n        this.imageThumbWidth = imageThumbWidth;\n    }\n\n    public short getImageThumbHeight() {\n        return imageThumbHeight;\n    }\n\n    public void setImageThumbHeight(short imageThumbHeight) {\n        this.imageThumbHeight = imageThumbHeight;\n    }\n\n    public String getFileId() {\n        return fileId;\n    }\n\n    public void setFileId(String fileId) {\n        this.fileId = fileId;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public void setFileName(String fileName) {\n        this.fileName = fileName;\n    }\n\n    public String getFileExtension() {\n        return fileExtension;\n    }\n\n    public void setFileExtension(String fileExtension) {\n        this.fileExtension = fileExtension;\n    }\n\n    public int getFileSize() {\n        return fileSize;\n    }\n\n    public void setFileSize(int fileSize) {\n        this.fileSize = fileSize;\n    }\n\n    public String getFilePath() {\n        return filePath;\n    }\n\n    public void setFilePath(String filePath) {\n        this.filePath = filePath;\n    }\n\n    public String getSenderRealAvatarUrl() {\n        if(\"http://\".equalsIgnoreCase(this.senderAvatarUrl) || \"https://\".equalsIgnoreCase(this.senderAvatarUrl)) {\n            return this.senderAvatarUrl;\n        }\n        if(this.senderAvatarUrl != null && !this.senderAvatarUrl.trim().isEmpty()) {\n            return this.senderAvatarUrl;\n        }\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return \"MessageDTO [id=\" + id + \", channelId=\" + channelId + \", channelType=\" + channelType + \", createAt=\"\n                + createAt + \", type=\" + type + \", senderId=\" + senderId + \", senderName=\" + senderName\n                + \", senderNickname=\" + senderNickname + \", senderOnlineStatus=\" + senderOnlineStatus\n                + \", senderAvatarUrl=\" + senderAvatarUrl + \", senderFirstLetterOfName=\" + senderFirstLetterOfName\n                + \", content=\" + content + \", fileId=\" + fileId + \", fileName=\" + fileName + \", fileExtension=\"\n                + fileExtension + \", fileSize=\" + fileSize + \", fileMimeType=\" + fileMimeType + \", imageWidth=\"\n                + imageWidth + \", imageHeight=\" + imageHeight + \", imageThumbWidth=\" + imageThumbWidth\n                + \", imageThumbHeight=\" + imageThumbHeight + \", filePath=\" + filePath + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/UserChannelDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * 用户频道dto类\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic class UserChannelDTO {\n\n    private String channelId;\n    \n    private String channelName;\n    \n    private String channelType;\n    \n    private String channelDisplayName;\n    \n    private String channelDescription;\n    \n    private String toUserId;\n    \n    private String toUserOnlineStatus;\n    \n    private short unreadMessageCount;\n    \n    private int memberCount;\n    \n    private String creatorId;\n\n    public String getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(String channelId) {\n        this.channelId = channelId;\n    }\n\n    public String getChannelName() {\n        return channelName;\n    }\n\n    public void setChannelName(String channelName) {\n        this.channelName = channelName;\n    }\n\n    public String getChannelType() {\n        return channelType;\n    }\n\n    public void setChannelType(String channelType) {\n        this.channelType = channelType;\n    }\n\n    public String getChannelDisplayName() {\n        return channelDisplayName;\n    }\n\n    public void setChannelDisplayName(String channelDisplayName) {\n        this.channelDisplayName = channelDisplayName;\n    }\n\n    public String getChannelDescription() {\n        return channelDescription;\n    }\n\n    public void setChannelDescription(String channelDescription) {\n        this.channelDescription = channelDescription;\n    }\n\n    public String getToUserId() {\n        return toUserId;\n    }\n\n    public void setToUserId(String toUserId) {\n        this.toUserId = toUserId;\n    }\n\n    public String getToUserOnlineStatus() {\n        return toUserOnlineStatus;\n    }\n\n    public void setToUserOnlineStatus(String toUserOnlineStatus) {\n        this.toUserOnlineStatus = toUserOnlineStatus;\n    }\n\n    public short getUnreadMessageCount() {\n        return unreadMessageCount;\n    }\n\n    public void setUnreadMessageCount(short unreadMessageCount) {\n        this.unreadMessageCount = unreadMessageCount;\n    }\n\n    public int getMemberCount() {\n        return memberCount;\n    }\n\n    public void setMemberCount(int memberCount) {\n        this.memberCount = memberCount;\n    }\n\n    public String getCreatorId() {\n        return creatorId;\n    }\n\n    public void setCreatorId(String creatorId) {\n        this.creatorId = creatorId;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserChannelDTO [channelId=\" + channelId + \", channelName=\" + channelName + \", channelType=\"\n                + channelType + \", channelDisplayName=\" + channelDisplayName + \", channelDescription=\"\n                + channelDescription + \", toUserId=\" + toUserId + \", toUserOnlineStatus=\" + toUserOnlineStatus\n                + \", unreadMessageCount=\" + unreadMessageCount + \", memberCount=\" + memberCount + \", creatorId=\"\n                + creatorId + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/dto/UserDTO.java",
    "content": "package org.leo.im.api.dto;\n\n/**\n * 用户dto类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class UserDTO {\n\n    private String id;\n\n    private String name;\n    \n    private String firstLetterOfName;\n\n    private String nickname;\n\n    private String password;\n    \n    private String avatarUrl;\n    \n    private Boolean locked;\n    \n    private Long lastPostAt;\n    \n    private String onlineStatus;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getFirstLetterOfName() {\n        return firstLetterOfName;\n    }\n\n    public void setFirstLetterOfName(String firstLetterOfName) {\n        this.firstLetterOfName = firstLetterOfName;\n    }\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public void setNickname(String nickname) {\n        this.nickname = nickname;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getAvatarUrl() {\n        return avatarUrl;\n    }\n\n    public void setAvatarUrl(String avatarUrl) {\n        this.avatarUrl = avatarUrl;\n    }\n\n    public Boolean getLocked() {\n        return locked;\n    }\n\n    public void setLocked(Boolean locked) {\n        this.locked = locked;\n    }\n\n    public Long getLastPostAt() {\n        return lastPostAt;\n    }\n\n    public void setLastPostAt(Long lastPostAt) {\n        this.lastPostAt = lastPostAt;\n    }\n\n    public String getOnlineStatus() {\n        return onlineStatus;\n    }\n\n    public void setOnlineStatus(String onlineStatus) {\n        this.onlineStatus = onlineStatus;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDTO [id=\" + id + \", name=\" + name + \", firstLetterOfName=\" + firstLetterOfName + \", nickname=\"\n                + nickname + \", password=\" + password + \", avatarUrl=\" + avatarUrl + \", locked=\" + locked\n                + \", lastPostAt=\" + lastPostAt + \", onlineStatus=\" + onlineStatus + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/exception/ServiceException.java",
    "content": "package org.leo.im.api.exception;\n\n/**\n * 服务异常类\n * \n * @author Leo\n * @date 2018/3/20\n */\npublic final class ServiceException extends RuntimeException {\n\n    private static final long serialVersionUID = 2447167264295739984L;\n\n    public ServiceException() {\n    }\n\n    public ServiceException(String message) {\n        super(message);\n    }\n\n    public ServiceException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public ServiceException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/service/ChannelService.java",
    "content": "package org.leo.im.api.service;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.leo.im.api.annotation.Transactional;\nimport org.leo.im.api.dto.ChannelDTO;\nimport org.leo.im.api.dto.ChannelListDTO;\nimport org.leo.im.api.dto.ChannelMemberDTO;\nimport org.leo.im.common.data.Page;\n\n/**\n * 频道管理服务接口\n * \n * @author Leo\n * @date 2018/4/11\n */\npublic interface ChannelService {\n    \n    /**\n     * 得到频道列表\n     * @param parameters\n     * @param limit\n     * @return\n     */\n    List<ChannelListDTO> listChannel(Map<String, Object> parameters, int limit);\n    \n    /**\n     * 得到群组频道列表\n     * @param parameters\n     * @param limit\n     * @return\n     */\n    List<ChannelListDTO> listGroupChannel(Map<String, Object> parameters, int limit);\n    \n    /**\n     * 添加频道\n     * @param dto\n     * @param creatorNickname\n     * @return\n     */\n    @Transactional\n    ChannelDTO saveChannel(ChannelDTO dto, String creatorNickname);\n    \n    /**\n     * 根据id得到频道信息\n     * @param id\n     * @return\n     */\n    ChannelDTO getById(String id);\n    \n    /**\n     * 得到用户是否为频道的管理员\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    boolean isAdmin(String userId, String channelId);\n    \n    /**\n     * 更新频道名称\n     * @param channelId\n     * @param name\n     * @return\n     */\n    @Transactional\n    int updateName(String channelId, String name);\n    \n    /**\n     * 更新频道用途\n     * @param channelId\n     * @param purpose\n     * @return\n     */\n    @Transactional\n    int updatePurpose(String channelId, String purpose);\n    \n    /**\n     * 添加频道成员\n     * @param channelId\n     * @param userIds\n     * @param userNicknames\n     * @param admin\n     * @return\n     */\n    @Transactional\n    int addMember(String channelId, String[] userIds, String[] userNicknames, String admin);\n    \n    /**\n     * 移除组成员\n     * @param channelId\n     * @param memberId\n     * @param memberNickname\n     * @param admin\n     * @return\n     */\n    @Transactional\n    int removeMember(String channelId, String memberId, String memberNickname, String admin);\n    \n    /**\n     * 得到成员列表\n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<ChannelMemberDTO> listMember(String channelId, String username, int limit, int offset);\n    \n    /**\n     * 变更频道管理员\n     * @param channelId\n     * @param memberId\n     * @param isAdmin\n     * @return\n     */\n    @Transactional\n    int changeAdmin(String channelId, String memberId, boolean isAdmin);\n    \n    /**\n     * 离开频道\n     * @param channelId\n     * @param memberId\n     * @param memberNickname\n     * @return\n     */\n    @Transactional\n    int leaveChannel(String channelId, String memberId, String memberNickname);\n    \n    /**\n     * 删除频道\n     * @param channelId\n     * @param adminId\n     * @return\n     */\n    @Transactional\n    int removeChannel(String channelId, String adminId);\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/service/MessageService.java",
    "content": "package org.leo.im.api.service;\n\nimport java.util.List;\n\nimport org.leo.im.api.annotation.Transactional;\nimport org.leo.im.api.dto.FileDTO;\nimport org.leo.im.api.dto.MessageDTO;\n\n/**\n * 消息服务接口\n * \n * @author Leo\n * @date 2018/5/16\n */\npublic interface MessageService {\n\n    /**\n     * 得到消息列表\n     * @param channelId\n     * @param maxCreateAt\n     * @param limit\n     * @return\n     */\n    List<MessageDTO> listMessage(String channelId, long maxCreateAt, int limit);\n    \n    /**\n     * 根据id得到消息\n     * @param id\n     * @return\n     */\n    MessageDTO getById(long id);\n    \n    /**\n     * 添加消息\n     * @param dto\n     * @return\n     */\n    @Transactional\n    MessageDTO saveMessage(MessageDTO dto);\n    \n    /**\n     * 批量添加消息\n     * @param dtos\n     * @return\n     */\n    @Transactional\n    int saveMessage(List<MessageDTO> dtos);\n    \n    /**\n     * 读取消息\n     * @param channelId\n     * @param userId\n     * @param total\n     * @return\n     */\n    @Transactional\n    int readMessage(String channelId, String userId, short total);\n    \n    /**\n     * 删除消息\n     * @param messageId\n     * @param senderId\n     * @param channelId\n     * @param toUserId\n     * @return\n     */\n    @Transactional\n    int removeMessage(long messageId, String senderId, String channelId, String toUserId);\n    \n    /**\n     * 添加文件\n     * @param dto\n     * @return\n     */\n    @Transactional\n    String saveFile(FileDTO dto);\n    \n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/service/UnreadMessageCountService.java",
    "content": "package org.leo.im.api.service;\n\nimport org.leo.im.api.annotation.Transactional;\n\n/**\n * 未读消息数量服务接口\n * @author Administrator\n *\n */\npublic interface UnreadMessageCountService {\n    \n    /**\n     * 批量添加未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Transactional\n    int batchSaveUnreadMessageCount(String[] userIds, String channelId, short total);\n    \n    /**\n     * 更新未读消息数量\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Transactional\n    int updateUnreadMessageCount(String userId, String channelId, short total);\n    \n    /**\n     * 批量更新未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int batchUpdateUnreadMessageCount(String[] userIds, String channelId, short total);\n    \n    /**\n     * 批量增加未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Transactional\n    int batchIncreaseUnreadMessageCount(String[] userIds, String channelId, short quantity);\n    \n    /**\n     * 增加未读消息数量\n     * @param userId\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Transactional\n    int increaseUnreadMessageCount(String userId, String channelId, short quantity);\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/service/UserChannelService.java",
    "content": "package org.leo.im.api.service;\n\nimport java.util.List;\n\nimport org.leo.im.api.annotation.Transactional;\nimport org.leo.im.api.dto.UserChannelDTO;\n\n/**\n * 用户频道服务接口\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic interface UserChannelService {\n    \n    /**\n     * 得到用户频道列表\n     * @param userId\n     * @param type\n     * @param limit\n     * @return\n     */\n    List<UserChannelDTO> listUserChannel(String userId, String type, int limit);\n    \n    /**\n     * 得到用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    UserChannelDTO get(String userId, String channelId);\n    \n    /**\n     * 更新用户频道\n     * @param channelId\n     * @param userId\n     * @param displayName\n     * @return\n     */\n    @Transactional\n    int updateDisplayName(String channelId, String userId, String displayName);\n    \n    /**\n     * 隐藏频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Transactional\n    int hideChannel(String userId, String channelId);\n    \n    /**\n     * 根据名称得到用户频道列表\n     * @param userId\n     * @param name\n     * @param type\n     * @return\n     */\n    List<UserChannelDTO> listByName(String userId, String name, String type);\n\n}\n"
  },
  {
    "path": "leo-im-api/src/main/java/org/leo/im/api/service/UserService.java",
    "content": "package org.leo.im.api.service;\n\nimport java.util.Set;\n\nimport org.leo.im.api.annotation.Transactional;\nimport org.leo.im.api.dto.UserDTO;\nimport org.leo.im.common.data.Page;\n\n/**\n * 用户服务接口\n * \n * @author Leo\n * @date 2018/4/9\n */\npublic interface UserService {\n\n    /**\n     * 验证用户登录\n     * \n     * @param loginName\n     * @param password\n     * @return\n     */\n    UserDTO verifyLogin(String loginName, String password);\n\n    /**\n     * 根据id得到用户\n     * \n     * @param id\n     * @return\n     */\n    UserDTO getById(String id);\n\n    /**\n     * 添加用户\n     * \n     * @param dto\n     * @return\n     */\n    @Transactional\n    String saveUser(UserDTO dto);\n\n    /**\n     * 根据名称或昵称分页查询用户\n     * \n     * @param name\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<UserDTO> listByNameOrNickname(String name, int limit, int offset);\n    \n    /**\n     * 更新用户\n     * @param dto\n     * @param updateNullValueField\n     * @return\n     */\n    @Transactional\n    UserDTO updateUser(UserDTO dto, boolean updateNullValueField);\n    \n    /**\n     * \n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<UserDTO> listNonMembers(String channelId, String username, int limit, int offset);\n    \n    /**\n     * 批量下线用户\n     * @param userIds\n     * @return\n     */\n    @Transactional\n    int batchOffline(Set<String> userIds);\n    \n    /**\n     * 修改用户口令\n     * @param userId\n     * @param username\n     * @param oldPassword\n     * @param newPassword\n     * @return\n     */\n    @Transactional\n    int updatePassword(String userId, String username, String oldPassword, String newPassword);\n\n}\n"
  },
  {
    "path": "leo-im-api/src/test/java/org/leo/im/api/AppTest.java",
    "content": "package org.leo.im.api;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-api/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: leo-im-common-1.0.jar slf4j-api-1.7.25.jar logback-classic\n -1.2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-api/target/classes/META-INF/maven/org.leo.im/leo-im-api/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:09 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-api\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-api\nartifactId=leo-im-api\n"
  },
  {
    "path": "leo-im-api/target/classes/META-INF/maven/org.leo.im/leo-im-api/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-api</artifactId>\n\t<name>leo-im-api</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-common</artifactId>\n            <version>${parent.version}</version>\n        </dependency>       \n    </dependencies>\t\n</project>\n"
  },
  {
    "path": "leo-im-api/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:09 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-api\n"
  },
  {
    "path": "leo-im-api/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-api/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\service\\UserChannelService.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\annotation\\Cacheable.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\service\\UnreadMessageCountService.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\service\\UserService.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\FileDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\UserDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\annotation\\Transactional.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\exception\\ServiceException.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\service\\MessageService.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\ChannelDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\ChannelListDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\ChannelMemberDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\MessageDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\dto\\UserChannelDTO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\main\\java\\org\\leo\\im\\api\\service\\ChannelService.java\n"
  },
  {
    "path": "leo-im-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-api/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-api\\src\\test\\java\\org\\leo\\im\\api\\AppTest.java\n"
  },
  {
    "path": "leo-im-api/target/surefire-reports/TEST-org.leo.im.api.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.api.AppTest\" time=\"0.002\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.api.AppTest\" name=\"testApp\" time=\"0.002\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-api/target/surefire-reports/org.leo.im.api.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.api.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.097 sec\n"
  },
  {
    "path": "leo-im-api-provider/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-api-provider/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-api-provider</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-api-provider/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-api-provider/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-api-provider/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-api-provider/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-api-provider</artifactId>\n\t<name>leo-im-api-provider</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-service</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-api-provider/src/main/java/org/leo/im/api/provider/ServiceFactory.java",
    "content": "package org.leo.im.api.provider;\n\nimport org.leo.im.api.service.ChannelService;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.api.service.UserChannelService;\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.service.ChannelServiceImpl;\nimport org.leo.im.service.MessageServiceImpl;\nimport org.leo.im.service.UserChannelServiceImpl;\nimport org.leo.im.service.UserServiceImpl;\n\n/**\n * 服务工厂类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic final class ServiceFactory {\n\n    /**\n     * 创建用户服务的实例\n     * \n     * @return\n     */\n    public static UserService createUserService() {\n        return new UserServiceImpl();\n    }\n    \n    /**\n     * 创建频道服务的实例\n     * @return\n     */\n    public static ChannelService createChannelService() {\n        return new ChannelServiceImpl();\n    }\n    \n    /**\n     * 创建用户频道服务类的实例\n     * @return\n     */\n    public static UserChannelService createUserChannelService() {\n        return new UserChannelServiceImpl();\n    }\n    \n    /**\n     * 创建消息服务的实例\n     * @return\n     */\n    public static MessageService createMessageService() {\n        return new MessageServiceImpl();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-api-provider/src/test/java/org/leo/im/api/provider/AppTest.java",
    "content": "package org.leo.im.api.provider;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-api-provider/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-service-1.\n 0.jar leo-im-store-1.0.jar mysql-connector-java-8.0.11.jar protobuf-j\n ava-2.6.0.jar druid-1.1.9.jar leo-im-model-1.0.jar leo-im-util-1.0.ja\n r cglib-3.2.6.jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jj\n wt-0.9.0.jar jackson-databind-2.8.9.jar jackson-annotations-2.8.0.jar\n  jackson-core-2.8.9.jar leo-im-notification-1.0.jar jedis-2.9.0.jar c\n ommons-pool2-2.4.2.jar fastjson-1.2.47.jar slf4j-api-1.7.25.jar logba\n ck-classic-1.2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-api-provider/target/classes/META-INF/maven/org.leo.im/leo-im-api-provider/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:17 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-api-provider\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-api-provider\nartifactId=leo-im-api-provider\n"
  },
  {
    "path": "leo-im-api-provider/target/classes/META-INF/maven/org.leo.im/leo-im-api-provider/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-api-provider</artifactId>\n\t<name>leo-im-api-provider</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-service</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-api-provider/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:18 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-api-provider\n"
  },
  {
    "path": "leo-im-api-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-api-provider/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-api-provider\\src\\main\\java\\org\\leo\\im\\api\\provider\\ServiceFactory.java\n"
  },
  {
    "path": "leo-im-api-provider/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-api-provider/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-api-provider\\src\\test\\java\\org\\leo\\im\\api\\provider\\AppTest.java\n"
  },
  {
    "path": "leo-im-api-provider/target/surefire-reports/TEST-org.leo.im.api.provider.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.api.provider.AppTest\" time=\"0.001\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.api.provider.AppTest\" name=\"testApp\" time=\"0.001\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-api-provider/target/surefire-reports/org.leo.im.api.provider.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.api.provider.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.087 sec\n"
  },
  {
    "path": "leo-im-common/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-common/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-common</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-common/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-common/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-common/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-common/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-common</artifactId>\n\t<name>leo-im-common</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n</project>\n"
  },
  {
    "path": "leo-im-common/src/main/java/org/leo/im/common/data/Page.java",
    "content": "package org.leo.im.common.data;\n\nimport java.util.List;\n\n/**\n * 分页查询结果类\n * \n * @author Leo\n * @date 2018/4/8\n * @param <T>\n */\npublic final class Page<T> implements java.io.Serializable {\n\n    private static final long serialVersionUID = 1L;\n\n    private long total;\n\n    private List<T> rows;\n\n    public Page(long total, List<T> rows) {\n        this.total = total;\n        this.rows = rows;\n    }\n\n    public long getTotal() {\n        return this.total;\n    }\n\n    public List<T> getRows() {\n        return this.rows;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-common/src/test/java/org/leo/im/common/AppTest.java",
    "content": "package org.leo.im.common;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-common/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-cor\n e-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-common/target/classes/META-INF/maven/org.leo.im/leo-im-common/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:08 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-common\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-common\nartifactId=leo-im-common\n"
  },
  {
    "path": "leo-im-common/target/classes/META-INF/maven/org.leo.im/leo-im-common/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-common</artifactId>\n\t<name>leo-im-common</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n</project>\n"
  },
  {
    "path": "leo-im-common/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:08 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-common\n"
  },
  {
    "path": "leo-im-common/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-common/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-common\\src\\main\\java\\org\\leo\\im\\common\\data\\Page.java\n"
  },
  {
    "path": "leo-im-common/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-common/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-common\\src\\test\\java\\org\\leo\\im\\common\\AppTest.java\n"
  },
  {
    "path": "leo-im-common/target/surefire-reports/TEST-org.leo.im.common.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.common.AppTest\" time=\"0.007\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.common.AppTest\" name=\"testApp\" time=\"0.007\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-common/target/surefire-reports/org.leo.im.common.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.common.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.084 sec\n"
  },
  {
    "path": "leo-im-http/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-http/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-http</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-http/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-http/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-http/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-http/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-http</artifactId>\n\t<name>leo-im-http</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.ow2.asm</groupId>\n\t\t\t<artifactId>asm</artifactId>\n\t\t\t<version>6.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>net.coobird</groupId>\n\t\t\t<artifactId>thumbnailator</artifactId>\n\t\t\t<version>0.4.8</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.netty</groupId>\n\t\t\t<artifactId>netty-all</artifactId>\n\t\t\t<version>${netty.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>${fastjson.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo</groupId>\n\t\t\t<artifactId>netty-rest-server</artifactId>\n\t\t\t<version>1.0</version>\n\t\t\t<!-- \n\t\t\t<scope>system</scope>\n\t\t\t<systemPath>${project.basedir}/lib/netty-rest-server-1.0.jar</systemPath>\n\t\t\t -->\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api-provider</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-notification</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/HttpServer.java",
    "content": "package org.leo.im.http;\n\nimport org.leo.im.http.controller.ExceptionController;\nimport org.leo.im.http.interceptor.AuthenticationInterceptor;\nimport org.leo.im.http.interceptor.CorsInterceptor;\nimport org.leo.web.core.WebServer;\n\n/**\n * Leo IM REST API Server\n * \n * @author Leo\n * @date 2018/4/2\n */\npublic final class HttpServer {\n    \n    /**\n     * 启动api server\n     * @throws InterruptedException \n     */\n    public void start() throws InterruptedException {\n        // 忽略指定url\n        WebServer.getIgnoreUrls().add(\"/favicon.ico\");\n        \n        // 全局异常处理\n        WebServer.setExceptionHandler(new ExceptionController());\n        \n        // 设置监听端口号\n        WebServer server = new WebServer(Integer.getInteger(\"http.port\"));\n        \n        // 设置boss与worker线程数\n        server.setBossThreads(Integer.getInteger(\"http.boss.threads\"));\n        server.setWorkerThreads(Integer.getInteger(\"http.worker.threads\"));\n        \n        // 设置Http最大内容长度（默认 为10M）\n        server.setMaxContentLength(1024 * 1024 * Integer.getInteger(\"http.max.content.length\"));\n        \n        // 设置Controller所在包\n        server.setControllerBasePackage(\"org.leo.im.http.controller\");\n        \n        // 添加拦截器，按照添加的顺序执行。\n        // 跨域拦截器\n        server.addInterceptor(new CorsInterceptor());\n        // 身份认证拦截器，设置例外url\n        server.addInterceptor(new AuthenticationInterceptor(), \"/auth/login\", \"/auth/verificationCode\", \n                \"/users\", \"/messages/files\");\n        \n        server.start();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/cache/Cache.java",
    "content": "package org.leo.im.http.cache;\n\n/**\n * 缓存\n * \n * @author Leo\n * @date 2018/4/1\n */\nfinal class Cache {\n    \n    private Object value;\n    \n    /**\n     * 缓存的生存时间，单位：秒\n     */\n    private int timeout;\n    \n    private long expireAt;\n    \n    public Cache(Object value) {\n        this.value = value;\n    }\n    \n    public Cache(Object value, int timeout) {\n        this.value = value;\n        this.timeout = timeout;\n        this.expireAt = System.currentTimeMillis() + this.timeout * 1000;\n    }\n    \n    public Object getValue() {\n        return this.value;\n    }\n    public void setValue(Object value) {\n        this.value = value;\n    }\n    \n    public int getTimeout() {\n        return this.timeout;\n    }\n    public void setTimeout(int timeout) {\n        this.timeout = timeout;\n        this.expireAt = System.currentTimeMillis() + this.timeout * 1000;\n    }\n    \n    public long getExpireAt() {\n        return this.expireAt;\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/cache/CacheManager.java",
    "content": "package org.leo.im.http.cache;\n\n/**\n * 缓存管理接口\n * \n * @author Leo\n * @date 2018/3/27\n */\npublic interface CacheManager {\n    \n    /**\n     * 得到缓存\n     * @param key\n     * @return\n     */\n    Object get(String key);\n\n    /**\n     * 设置缓存\n     * @param key\n     * @param value\n     */\n    void put(String key, Object value);\n    \n    /**\n     * 设置缓存\n     * @param key\n     * @param value\n     * @param timeout 生存时间，单位为秒。\n     */\n    void put(String key, Object value, int timeout);\n    \n    /**\n     * 删除缓存\n     * @param key\n     */\n    void remove(String key);\n    \n    /**\n     * 清除缓存\n     */\n    void clear();\n    \n    /**\n     * 清理超时缓存\n     */\n    void clearExpired();\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/cache/CacheManagerFactory.java",
    "content": "package org.leo.im.http.cache;\n\n/**\n * 缓存管理器工厂类\n * \n * @author Leo\n * @date 2018/3/27\n */\npublic final class CacheManagerFactory {\n\n    /**\n     * 得到缓存管理器的实例\n     * \n     * @return\n     */\n    public static CacheManager getCacheManager() {\n        String cacheType = System.getProperty(\"cache.type\") == null ? \"map\"\n                : System.getProperty(\"cache.type\").toLowerCase();\n        switch (cacheType) {\n        case \"map\":\n            return MapCacheManager.getInstance();\n        default:\n            return MapCacheManager.getInstance();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/cache/MapCacheManager.java",
    "content": "package org.leo.im.http.cache;\n\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 基于Java Map的缓存管理器\n * \n * @author Leo\n * @date 2018/3/27\n */\nfinal class MapCacheManager implements CacheManager {\n\n    private final static int CLEAR_INTERVAL = 10;\n\n    private Map<String, Cache> caches = new ConcurrentHashMap<>(64);\n\n    private MapCacheManager() {\n\n    }\n    \n    public static class InstanceHolder {\n        private static MapCacheManager instance = new MapCacheManager();\n        static {\n            instance.clearExpired();\n        }\n    }\n    \n    public static MapCacheManager getInstance() {\n        return InstanceHolder.instance;\n    }\n    \n    /**\n     * 得到缓存\n     * @param key\n     * @return\n     */\n    @Override\n    public Object get(String key) {\n        Cache cache = caches.get(key);\n        return cache == null ? null : cache.getValue();\n    }\n\n    /**\n     * 添加缓存\n     * \n     * @param key\n     * @param value\n     */\n    @Override\n    public void put(String key, Object value) {\n        Cache cache = new Cache(value);\n        caches.put(key, cache);\n    }\n\n    /**\n     * 添加缓存\n     * \n     * @param key\n     * @param value\n     * @param timeout\n     *            生存时间，单位为秒。\n     */\n    @Override\n    public void put(String key, Object value, int timeout) {\n        Cache cache = new Cache(value, timeout);\n        caches.put(key, cache);\n    }\n\n    /**\n     * 删除缓存\n     * \n     * @param key\n     */\n    @Override\n    public void remove(String key) {\n        this.caches.remove(key);\n    }\n\n    /**\n     * 清除缓存\n     */\n    @Override\n    public void clear() {\n        this.caches.clear();\n    }\n\n    /**\n     * 清理超时缓存\n     */\n    @Override\n    public void clearExpired() {\n        Timer timer = new Timer();\n        timer.schedule(new TimerTask() {\n\n            @Override\n            public void run() {\n                Set<Entry<String, Cache>> entrySet = caches.entrySet();\n                long currentTime = System.currentTimeMillis();\n                for (Entry<String, Cache> entry : entrySet) {\n                    if (entry.getValue().getExpireAt() > 0 && currentTime >= entry.getValue().getExpireAt()) {\n                        caches.remove(entry.getKey());\n                    }\n                }\n            }\n\n        }, 1 * 1000, CLEAR_INTERVAL * 1000);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/constant/CacheKeys.java",
    "content": "package org.leo.im.http.constant;\n\n/**\n * 缓存key常量类\n * \n * @author Leo\n * @date 2018/4/1\n */\npublic final class CacheKeys {\n\n    /**\n     * JSession key前缀\n     */\n    public static final String JSESSIONID = \"JSESSIONID\";\n    \n    /**\n     * 验证码缓存key前缀\n     */\n    public static final String VERIFICATION_CODE_PREFIX = \"v-code-\"; \n    \n    /**\n     * 登录失败次数缓存前缀\n     */\n    public static final String LOGIN_FAILURE_COUNT = \"login-failure-count_\";\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/AuthController.java",
    "content": "package org.leo.im.http.controller;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\n\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.api.dto.UserDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.http.cache.CacheManagerFactory;\nimport org.leo.im.http.constant.CacheKeys;\nimport org.leo.im.http.util.JwtUtils;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.web.annotation.GetMapping;\nimport org.leo.web.annotation.PostMapping;\nimport org.leo.web.annotation.RequestMapping;\nimport org.leo.web.annotation.RestController;\nimport org.leo.web.annotation.UrlEncodedForm;\nimport org.leo.web.rest.HttpResponse;\nimport org.leo.web.rest.HttpStatus;\nimport org.leo.web.rest.ResponseEntity;\n\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.netty.handler.codec.http.cookie.ServerCookieDecoder;\nimport io.netty.handler.codec.http.FullHttpRequest;\nimport io.netty.handler.codec.http.cookie.Cookie;\n\n/**\n * 认证控制器\n * \n * @author Leo\n * @date 2018/4/4\n */\n@RestController\n@RequestMapping(\"/auth\")\npublic final class AuthController extends BaseController {\n    \n    /**\n     * Session超时时间，单位：秒。\n     */\n    private static final int SESSION_TIMEOUT = 5 * 60;\n    \n    /**\n     * 验证码超时时间，默认为120秒。\n     */\n    private static final int VERIFICATION_CODE_TIMEOUT = 2 * 60;\n    \n    /**\n     * 登录重试次数\n     */\n    private static final int LOGIN_RETRY_COUNT = 5;\n    \n    /**\n     * 登录失败次数缓存超时时间，单位：秒\n     */\n    private static final int LOGIN_FAILURE_COUNT_TIMEOUT = 5 * 60;\n\n    @PostMapping(\"/login\")\n    public ResponseEntity<?> login(FullHttpRequest request, HttpResponse response, @UrlEncodedForm Map<String, String> form) {\n        String sessionId = getJSessionId(request);\n        if(sessionId == null) {\n            // Session不存在\n            sessionId = UUID.randomUUID().toString().replaceAll(\"-\", \"\");\n            response.getCookies().put(CacheKeys.JSESSIONID, sessionId);\n        }\n        CacheManagerFactory.getCacheManager().put(sessionId, (byte)0, SESSION_TIMEOUT);\n        \n        // 检查登录失败次数\n        Object loginFailureCount = CacheManagerFactory.getCacheManager().get(CacheKeys.LOGIN_FAILURE_COUNT + sessionId);\n        if(loginFailureCount != null) {\n            if((int)loginFailureCount >= LOGIN_RETRY_COUNT) {\n                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(\"登录失败次数过多，请稍后重试\");\n            }\n        }\n        \n        // 检查验证码\n        /*String verificationCode = form.get(\"verificationCode\");\n        if(verificationCode == null || verificationCode.trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"验证码为空\");\n        }\n        if(sessionId == null || sessionId.trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的请求\");\n        }\n        Object verificationCodeInCache = CacheManagerFactory.getCacheManager().get(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId);\n        if(verificationCodeInCache == null || verificationCodeInCache.toString().trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的验证码\");\n        }\n        if(!verificationCode.equals(verificationCodeInCache)) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的验证码\");\n        }*/\n        \n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        UserDTO dto = serviceProxy.verifyLogin(form.get(\"username\"), form.get(\"password\"));        \n        if (dto == null) {\n            int userLoginFailureCount = (loginFailureCount == null ? 0 : (int)loginFailureCount);\n            CacheManagerFactory.getCacheManager().put(CacheKeys.LOGIN_FAILURE_COUNT + sessionId, ++userLoginFailureCount, LOGIN_FAILURE_COUNT_TIMEOUT);\n            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); \n        }\n        \n        JSONObject json = new JSONObject();\n        json.put(\"userId\", dto.getId());\n        String jwt = JwtUtils.createJWT(json.toJSONString(), System.getProperty(\"jwt.secret\"), Long.getLong(\"jwt.ttl.millis\"));\n        Map<String, String> result = new HashMap<>();\n        result.put(\"userId\", dto.getId());\n        result.put(\"username\", dto.getName());\n        result.put(\"nickname\", dto.getNickname());\n        result.put(\"firstLetterOfName\", dto.getFirstLetterOfName());\n        result.put(\"avatarUrl\", dto.getAvatarUrl());\n        result.put(\"token\", jwt);\n        \n        // 删除缓存中的验证码\n        // CacheManagerFactory.getCacheManager().remove(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId);\n        // 删除登录失败次数缓存\n        CacheManagerFactory.getCacheManager().remove(CacheKeys.LOGIN_FAILURE_COUNT + sessionId);\n        return ResponseEntity.ok(result);\n    }\n\n    @GetMapping(\"/verificationCode\")\n    public ResponseEntity<String> getVerificationCode(FullHttpRequest request, HttpResponse response) {\n        String verificationCode = UUID.randomUUID().toString().substring(0, 4);\n        String sessionId = null;\n        if((sessionId = getJSessionId(request)) == null) {\n            // Session不存在\n            sessionId = UUID.randomUUID().toString().replaceAll(\"-\", \"\");\n            response.getCookies().put(CacheKeys.JSESSIONID, sessionId);\n        }\n        CacheManagerFactory.getCacheManager().put(sessionId, (byte)0, SESSION_TIMEOUT);\n        CacheManagerFactory.getCacheManager().put(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId, verificationCode, VERIFICATION_CODE_TIMEOUT);\n        return ResponseEntity.ok(verificationCode);\n    }\n\n    /**\n     * 从cookie中得到Session Id\n     * @param request\n     * @return\n     */\n    private String getJSessionId(FullHttpRequest request) {\n        try {\n            String cookieStr = request.headers().get(\"Cookie\");\n            if(cookieStr == null || cookieStr.trim().isEmpty()) {\n                return null;\n            }\n            Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieStr);\n            Iterator<Cookie> it = cookies.iterator();\n\n            while (it.hasNext()) {\n                Cookie cookie = it.next();\n                if (cookie.name().equals(CacheKeys.JSESSIONID)) {\n                    if (CacheManagerFactory.getCacheManager().get(cookie.value()) != null) {\n                        return cookie.value();\n                    }\n                }\n            }\n        } catch (Exception e1) {\n            return null;\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/BaseController.java",
    "content": "package org.leo.im.http.controller;\n\nimport org.leo.im.http.util.JwtUtils;\n\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.jsonwebtoken.Claims;\n\n/**\n * 控制器基类\n * \n * @author Leo\n * @date 2018/3/26\n */\npublic class BaseController {\n    \n    /**\n     * 从jwt中获取subject信息\n     * @param jwtUtils\n     * @param jwt\n     * @param key\n     * @return\n     */\n    protected String getSubjectFromJwt(String jwt, String key) {\n        Claims claims = JwtUtils.parseJWT(jwt, System.getProperty(\"jwt.secret\"));\n        String subject = claims.getSubject();\n        if(key != null && !key.trim().equals(\"\")) {\n            JSONObject json = JSONObject.parseObject(subject);\n            return json.getString(key);\n        } else {\n            return subject;\n        }\n    }\n    \n    /**\n     * 验证jwt\n     * @param jwt\n     * @return\n     */\n    protected boolean verifyJwt(String jwt) {\n        try {\n            JwtUtils.parseJWT(jwt, System.getProperty(\"jwt.secret\"));\n            return true;\n        } catch(Exception e) {\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/ChannelController.java",
    "content": "package org.leo.im.http.controller;\n\nimport org.leo.im.api.dto.ChannelDTO;\nimport org.leo.im.api.dto.ChannelMemberDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.ChannelService;\nimport org.leo.im.common.data.Page;\nimport org.leo.im.http.vo.ChannelVO;\nimport org.leo.im.http.vo.UserChannelVO;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.im.util.BeanUtils;\nimport org.leo.web.annotation.DeleteMapping;\nimport org.leo.web.annotation.GetMapping;\nimport org.leo.web.annotation.PatchMapping;\nimport org.leo.web.annotation.PathVariable;\nimport org.leo.web.annotation.PostMapping;\nimport org.leo.web.annotation.PutMapping;\nimport org.leo.web.annotation.RequestBody;\nimport org.leo.web.annotation.RequestHeader;\nimport org.leo.web.annotation.RequestMapping;\nimport org.leo.web.annotation.RequestParam;\nimport org.leo.web.annotation.RestController;\nimport org.leo.web.rest.ResponseEntity;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.netty.handler.codec.http.FullHttpRequest;\n\n/**\n * 频道控制器\n * \n * @author Leo\n * @date 2018/4/3\n */\n@RestController\n@RequestMapping(\"/channels\")\npublic final class ChannelController extends BaseController {\n\n    @PostMapping(\"\")\n    public ResponseEntity<UserChannelVO> createChannel(FullHttpRequest request, @RequestHeader(\"X-Token\") String token,\n            @RequestBody String body) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        ChannelDTO dto = new ChannelDTO();\n        JSONObject json = JSONObject.parseObject(body);\n        dto.setType(json.getString(\"type\"));\n        if (\"G\".equals(dto.getType())) {\n            dto.setName(json.getString(\"name\"));\n            dto.setPurpose(json.getString(\"purpose\"));\n            JSONArray members = json.getJSONArray(\"members\");\n            dto.setMemberCount(members.size());\n            for(int i = 0; i < members.size(); i++) {\n                ChannelMemberDTO member = new ChannelMemberDTO();\n                JSONObject memberJson = members.getJSONObject(i);\n                member.setId(memberJson.getString(\"id\"));\n                member.setNickname(memberJson.getString(\"nickname\"));\n                member.setAdmin(memberJson.getString(\"id\").equals(userId));\n                dto.getMembers().add(member);\n            }\n        } \n        if (\"P\".equals(dto.getType())) {\n            dto.setFromUserId(userId);\n            dto.setFromUsername(json.getString(\"fromUsername\"));\n            dto.setFromUserNickname(json.getString(\"fromUserNickname\"));\n            dto.setToUserId(json.getString(\"toUserId\"));\n            dto.setToUsername(json.getString(\"toUsername\"));\n            dto.setToUserNickname(json.getString(\"toUserNickname\"));\n            dto.setName(dto.getToUserNickname() != null && !dto.getToUserNickname().trim().isEmpty() ? \n                    dto.getToUserNickname() : dto.getToUsername());\n            dto.setMemberCount(2);\n            ChannelMemberDTO member1 = new ChannelMemberDTO();\n            member1.setId(userId);\n            member1.setAdmin(true);\n            dto.getMembers().add(member1);\n            ChannelMemberDTO member2 = new ChannelMemberDTO();\n            member2.setId(dto.getToUserId());\n            dto.getMembers().add(member2);\n        }\n        dto.setCreatorId(userId);\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        ChannelDTO returnDTO = serviceProxy.saveChannel(dto, json.getString(\"creatorNickname\"));\n        UserChannelVO vo = new UserChannelVO();\n        BeanUtils.copyProperties(returnDTO, vo);\n        vo.setChannelId(returnDTO.getId());\n        vo.setChannelName(returnDTO.getName());\n        vo.setChannelDisplayName(getChannelDisplayName(returnDTO, userId));\n        vo.setChannelType(returnDTO.getType());\n        return ResponseEntity.created(vo);\n    }\n    \n    @GetMapping(\"/{id}\")\n    public ResponseEntity<ChannelVO> getById(@PathVariable(\"id\") String id) {\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        ChannelDTO dto = serviceProxy.getById(id);\n        if(dto == null) {\n            return ResponseEntity.notFound().build();\n        }\n        ChannelVO vo = new ChannelVO();\n        BeanUtils.copyProperties(dto, vo);\n        return ResponseEntity.ok(vo);\n    }\n    \n    \n    @GetMapping(\"/{channelId}/isAdmin\")\n    public ResponseEntity<Boolean> isAdmin(@PathVariable(\"channelId\") String channelId, @RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        boolean isAdmin = serviceProxy.isAdmin(userId, channelId);\n        return ResponseEntity.ok(isAdmin);\n    }\n    \n    @PatchMapping(\"/{channelId}\")\n    public ResponseEntity<?> updateChannel(@PathVariable(\"channelId\") String channelId, @RequestBody String body) {\n        JSONObject json = JSONObject.parseObject(body);\n        ChannelService serviceProxy = null;\n        if(json.containsKey(\"name\")) {\n            serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n            int count = serviceProxy.updateName(channelId, json.getString(\"name\"));\n            return ResponseEntity.ok(count);\n        }\n        if(json.containsKey(\"purpose\")) {\n            serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n            int count = serviceProxy.updatePurpose(channelId, json.getString(\"purpose\"));\n            return ResponseEntity.ok(count);\n        }\n        return ResponseEntity.internalServerError(\"not found allowed filed in body\");\n    }\n    \n    @GetMapping(\"/{channelId}/members\")\n    public ResponseEntity<Page<ChannelMemberDTO>> listMember(@PathVariable(\"channelId\") String channelId, \n            @RequestParam(\"username\") String username, @RequestParam(\"limit\") int limit, @RequestParam(\"offset\") int offset) {\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        Page<ChannelMemberDTO> result = serviceProxy.listMember(channelId, username, limit, offset);\n        return ResponseEntity.ok(result);\n    }\n    \n    @PostMapping(\"/{channelId}/members\")\n    public ResponseEntity<Integer> addMember(@PathVariable(\"channelId\") String channelId, @RequestBody String body) {\n        JSONObject data = JSONObject.parseObject(body);\n        JSONArray jsonArray = data.getJSONArray(\"users\");\n        String[] userIds = new String[jsonArray.size()];\n        String[] userNicknames = new String[jsonArray.size()];\n        for(int i = 0; i < userIds.length; i++) {\n            userIds[i] = jsonArray.getJSONObject(i).getString(\"id\");\n            userNicknames[i] = jsonArray.getJSONObject(i).getString(\"nickname\");\n        }\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        int count = serviceProxy.addMember(channelId, userIds, userNicknames, data.getString(\"admin\"));\n        return ResponseEntity.created(count);\n    }\n    \n    @DeleteMapping(\"/{channelId}/members\")\n    public ResponseEntity<Integer> removeMember(@PathVariable(\"channelId\") String channelId, @RequestBody String body) {\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        JSONObject data = JSONObject.parseObject(body);\n        int count = serviceProxy.removeMember(channelId, data.getString(\"memberId\"), data.getString(\"memberNickname\"),\n                data.getString(\"admin\"));\n        if(count > 0) {\n            return ResponseEntity.noContent(count);\n        }\n        return ResponseEntity.notFound().build();\n    }\n    \n    @PutMapping(\"/{channelId}/admin\")\n    public ResponseEntity<Integer> changeAdmin(@PathVariable(\"channelId\") String channelId, @RequestBody String body) {\n        JSONObject json = JSONObject.parseObject(body);\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        int count = serviceProxy.changeAdmin(channelId, json.getString(\"memberId\"), json.getBooleanValue(\"isAdmin\"));\n        return ResponseEntity.created(count);\n    }\n    \n    /**\n     * 离开频道\n     * @param channelId\n     * @param body\n     * @return\n     */\n    @DeleteMapping(\"/{channelId}/members/{memberId}\")\n    public ResponseEntity<Integer> leaveChannel(@PathVariable(\"channelId\") String channelId, @PathVariable(\"memberId\") String memberId,\n            @RequestBody String body) {\n        JSONObject data = JSONObject.parseObject(body);\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        int count = serviceProxy.leaveChannel(channelId, memberId, data.getString(\"memberNickname\"));\n        return ResponseEntity.noContent(count);\n    }\n    \n    @DeleteMapping(\"/{channelId}\")\n    public ResponseEntity<Integer> removeChannel(@PathVariable(\"channelId\") String channelId, @RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n        int count = serviceProxy.removeChannel(channelId, userId);\n        return ResponseEntity.noContent(count);\n    }\n    \n    /**\n     * 得到频道的显示名称\n     * @param dto\n     * @param creatorId\n     * @return\n     */\n    private String getChannelDisplayName(ChannelDTO dto, String creatorId) {\n        if(dto.getType().equals(\"G\")) {\n            return dto.getName();\n        }\n        if(dto.getCreatorId().equals(creatorId)) {\n            return dto.getToUserNickname();\n        }\n        return dto.getFromUserNickname();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/ExceptionController.java",
    "content": "package org.leo.im.http.controller;\n\nimport org.leo.web.exception.ResourceNotFoundException;\nimport org.leo.web.rest.HttpContextHolder;\nimport org.leo.web.rest.HttpResponse;\nimport org.leo.web.rest.HttpStatus;\nimport org.leo.web.rest.controller.ExceptionHandler;\n\n/**\n * 异常处理器\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class ExceptionController implements ExceptionHandler {\n\n    /**\n     * 处理异常\n     * \n     * @param e\n     */\n    @Override\n    public void doHandle(Exception e) {\n        HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;\n        if (e instanceof ResourceNotFoundException) {\n            status = HttpStatus.NOT_FOUND;\n        }\n        String errorMessage = e.getCause() == null ? \"\" : e.getCause().getMessage();\n        if (errorMessage == null) {\n            errorMessage = e.getMessage();\n        }\n        HttpResponse response = HttpContextHolder.getResponse();\n        response.write(status, errorMessage);\n        response.closeChannel();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/MessageController.java",
    "content": "package org.leo.im.http.controller;\n\nimport java.io.RandomAccessFile;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\n\nimport org.leo.im.api.dto.FileDTO;\nimport org.leo.im.api.dto.MessageDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.http.file.FileStorage;\nimport org.leo.im.http.file.FileStorageFactory;\nimport org.leo.im.http.vo.MessageVO;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.im.util.BeanUtils;\nimport org.leo.web.annotation.DeleteMapping;\nimport org.leo.web.annotation.GetMapping;\nimport org.leo.web.annotation.PostMapping;\nimport org.leo.web.annotation.RequestBody;\nimport org.leo.web.annotation.RequestHeader;\nimport org.leo.web.annotation.RequestMapping;\nimport org.leo.web.annotation.RequestParam;\nimport org.leo.web.annotation.RestController;\nimport org.leo.web.annotation.UploadFile;\nimport org.leo.web.annotation.UrlEncodedForm;\nimport org.leo.web.multipart.MultipartFile;\nimport org.leo.web.rest.ResponseEntity;\n\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.netty.handler.codec.http.FullHttpRequest;\n\n/**\n * 消息控制器\n * \n * @author Leo\n * @date 2018/5/16\n */\n@RestController()\n@RequestMapping(\"/messages\")\npublic final class MessageController extends BaseController {\n\n    @GetMapping(\"\")\n    public ResponseEntity<List<MessageVO>> listMessage(@RequestParam(\"channelId\") String channelId,\n            @RequestParam(\"maxCreateAt\") long maxCreateAt, @RequestParam(\"limit\") int limit) {\n        MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n        List<MessageDTO> dtoList = serviceProxy.listMessage(channelId, maxCreateAt, limit);\n        List<MessageVO> voList = new ArrayList<>(dtoList.size());\n        for(MessageDTO dto : dtoList) {\n            MessageVO vo = new MessageVO();\n            BeanUtils.copyProperties(dto, vo);\n            voList.add(vo);\n        }\n        return ResponseEntity.ok(voList);\n    }\n    \n    @PostMapping(\"\")\n    public ResponseEntity<?> saveMessage(FullHttpRequest request, @RequestHeader(\"X-Token\") String token,\n            @RequestBody String body) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        JSONObject json = JSONObject.parseObject(body);\n        MessageDTO dto = new MessageDTO();\n        dto.setSenderId(userId);\n        if(json.containsKey(\"type\")) {\n            dto.setType(json.getString(\"type\"));\n        }\n        dto.setChannelId(json.getString(\"channelId\"));\n        dto.setContent(json.getString(\"content\"));\n        dto.setCreateAt(new Date().getTime());\n        MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n        MessageDTO returnDTO = serviceProxy.saveMessage(dto);\n        if(returnDTO != null) {\n            MessageVO vo = new MessageVO();\n            BeanUtils.copyProperties(returnDTO, vo);\n            return ResponseEntity.created(vo);\n        }\n        return ResponseEntity.notFound().build();\n    }\n    \n    @PostMapping(\"/read\")\n    public ResponseEntity<?> readMessage(@RequestHeader(\"X-Token\") String token, @RequestBody String body) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        JSONObject json = JSONObject.parseObject(body);\n        MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n        int count = serviceProxy.readMessage(json.getString(\"channelId\"), userId, json.getShortValue(\"total\"));\n        return ResponseEntity.created(count);\n    }\n    \n    @DeleteMapping(\"\")\n    public ResponseEntity<?> removeMessage(@RequestHeader(\"X-Token\") String token, @RequestBody String body) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n        JSONObject json = JSONObject.parseObject(body);\n        int count = serviceProxy.removeMessage(json.getLongValue(\"messageId\"), userId, json.getString(\"channelId\"),\n                json.getString(\"toUserId\"));\n        if(count == 1) {\n            return ResponseEntity.noContent(count);\n        }\n        return ResponseEntity.notFound().build();\n    }\n    \n    @PostMapping(\"/files\")\n    public ResponseEntity<?> uploadFile(@UploadFile MultipartFile file, @RequestHeader(\"X-Token\") String token,\n            @UrlEncodedForm Map<String, String> form) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMdd\");\n        String fileKey = sdf.format(new Date()) + \"/\" + UUID.randomUUID().toString().replace(\"-\", \"\");\n        \n        // 保存文件\n        FileStorage fs = FileStorageFactory.newInstance();\n        fs.save(fileKey, file.getFileName(), file.getFileData());\n        \n        int width = Integer.parseInt(form.get(\"imageWidth\"));\n        int height = Integer.parseInt(form.get(\"imageHeight\"));\n        short thumbWidth = getThumbWidth(width);\n        short thumbHeight = getThumbHeight(height);\n        short[] realThumbSize = new short[] { 0, 0 };\n        // 如果是图片，保存略缩图\n        if(this.isImage(file.getFileType())) {\n            realThumbSize = fs.saveThumb(fileKey + \"/thumb\", file.getFileName(), file.getFileData(), thumbWidth, thumbHeight);\n        }\n        \n        // 保存文件信息到数据库\n        MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n        FileDTO fileDTO = new FileDTO();\n        fileDTO.setName(file.getFileName());\n        fileDTO.setExtension(getFileExtension(file.getFileName()));\n        fileDTO.setSize(Integer.parseInt(form.get(\"size\")));\n        fileDTO.setMimeType(file.getFileType());\n        fileDTO.setWidth(width);\n        fileDTO.setHeight(height);\n        fileDTO.setThumbWidth(realThumbSize[0]);\n        fileDTO.setThumbHeight(realThumbSize[1]);\n        fileDTO.setPath(fileKey);\n        String fileId = serviceProxy.saveFile(fileDTO);\n        \n        // 保存消息\n        MessageDTO messageDTO = new MessageDTO();\n        messageDTO.setSenderId(userId);\n        messageDTO.setChannelId(form.get(\"channelId\"));\n        messageDTO.setCreateAt(new Date().getTime());\n        messageDTO.setFileId(fileId);\n        MessageDTO returnDTO = serviceProxy.saveMessage(messageDTO);\n        if(returnDTO != null) {\n            MessageVO vo = new MessageVO();\n            BeanUtils.copyProperties(returnDTO, vo);\n            return ResponseEntity.created(vo);\n        }\n        return ResponseEntity.notFound().build();\n    }\n    \n    @GetMapping(\"/files\")\n    public ResponseEntity<RandomAccessFile> getFile(@RequestParam(\"fileName\") String fileName, @RequestParam(\"fullPath\") String fullPath, \n            @RequestParam(\"mimetype\") String mimetype) {\n        FileStorage fs = FileStorageFactory.newInstance();\n        RandomAccessFile raf = fs.read(fullPath);\n        return ResponseEntity.ok(raf, mimetype, fileName);\n    }\n    \n    /**\n     * 判断是否为图片类型\n     * @param fileType\n     * @return\n     */\n    private boolean isImage(String fileType) {\n        if(\"image/jpeg\".equalsIgnoreCase(fileType)) {\n            return true;\n        }\n        if(\"image/png\".equalsIgnoreCase(fileType)) {\n            return true;\n        }\n        return false;\n    }\n    \n    /**\n     * 得到缩略图宽度\n     * @param width\n     * @return\n     */\n    private short getThumbWidth(int width) {\n        if(width <= 119) {\n            return (short)width;\n        }\n        return 119;\n    }\n    \n    /**\n     * 得到缩略图宽度\n     * @param width\n     * @return\n     */\n    private short getThumbHeight(int height) {\n        if(height <= 81) {\n            return (short)height;\n        }\n        return 81;\n    }\n    \n    /**\n     * 得到文件扩展名\n     * @param fileName\n     * @return\n     */\n    private String getFileExtension(String fileName) {\n        String[] nameSplit = fileName.split(\"[.]\");\n        return nameSplit[nameSplit.length - 1];\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/UserChannelController.java",
    "content": "package org.leo.im.http.controller;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.UserChannelDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.UserChannelService;\nimport org.leo.im.http.vo.UserChannelVO;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.im.util.BeanUtils;\nimport org.leo.web.annotation.GetMapping;\nimport org.leo.web.annotation.PatchMapping;\nimport org.leo.web.annotation.PathVariable;\nimport org.leo.web.annotation.PostMapping;\nimport org.leo.web.annotation.RequestBody;\nimport org.leo.web.annotation.RequestHeader;\nimport org.leo.web.annotation.RequestMapping;\nimport org.leo.web.annotation.RequestParam;\nimport org.leo.web.annotation.RestController;\nimport org.leo.web.rest.ResponseEntity;\n\n@RestController\n@RequestMapping(\"/userChannels\")\npublic final class UserChannelController extends BaseController {\n    \n    @GetMapping(\"/{userId}\")\n    public ResponseEntity<List<UserChannelVO>> listUserChannel(@PathVariable(\"userId\") String userId, \n            @RequestParam(\"limit\") int limit) {\n        UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService());\n        List<UserChannelDTO> dtoList = serviceProxy.listUserChannel(userId, null, limit);\n        if(dtoList.isEmpty()) {\n            return ResponseEntity.ok(new ArrayList<UserChannelVO>());\n        }\n        List<UserChannelVO> voList = new ArrayList<>(dtoList.size());\n        for(UserChannelDTO dto : dtoList) {\n            UserChannelVO vo = new UserChannelVO();\n            BeanUtils.copyProperties(dto, vo);\n            voList.add(vo);\n        }\n        return ResponseEntity.ok(voList);\n    }\n    \n    @GetMapping(\"\")\n    public ResponseEntity<UserChannelVO> getUserChannel(@RequestParam(\"userId\") String userId, \n            @RequestParam(\"channelId\") String channelId) {\n        UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService());\n        UserChannelDTO dto = serviceProxy.get(userId, channelId);\n        if(dto == null) {\n            return ResponseEntity.notFound().build();\n        }\n        UserChannelVO vo = new UserChannelVO();\n        BeanUtils.copyProperties(dto, vo);\n        return ResponseEntity.ok(vo);\n    }\n    \n    /**\n     * 修改频道显示名称\n     * @param body\n     * @return\n     */\n    @PatchMapping(\"/{channelId}\")\n    public ResponseEntity<Integer> updateDisplayName(@PathVariable(\"channelId\") String channelId, @RequestBody String displayName, \n            @RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService());\n        int count = serviceProxy.updateDisplayName(channelId, userId, displayName);\n        return ResponseEntity.created(count);\n    }\n    \n    /**\n     * 隐藏频道\n     * @param channelId\n     * @param token\n     * @return\n     */\n    @PostMapping(\"/{channelId}/hiding\")\n    public ResponseEntity<Integer> hideChannel(@PathVariable(\"channelId\") String channelId, @RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService());\n        int count = serviceProxy.hideChannel(userId, channelId);\n        return ResponseEntity.created(count);\n    }\n    \n    /**\n     * 根据名称搜索用户频道\n     * @param userId\n     * @param name\n     * @return\n     */\n    @GetMapping(\"/{userId}/search\")\n    public ResponseEntity<List<UserChannelVO>> listUserChannelByName(@PathVariable(\"userId\") String userId, \n            @RequestParam(\"name\") String name) {\n        UserChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserChannelService());\n        List<UserChannelDTO> dtoList = serviceProxy.listByName(userId, name, null);\n        if(dtoList.isEmpty()) {\n            return ResponseEntity.ok(new ArrayList<UserChannelVO>());\n        }\n        List<UserChannelVO> voList = new ArrayList<>(dtoList.size());\n        for(UserChannelDTO dto : dtoList) {\n            UserChannelVO vo = new UserChannelVO();\n            BeanUtils.copyProperties(dto, vo);\n            voList.add(vo);\n        }\n        return ResponseEntity.ok(voList);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/controller/UserController.java",
    "content": "package org.leo.im.http.controller;\n\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.common.data.Page;\nimport org.leo.im.http.cache.CacheManagerFactory;\nimport org.leo.im.http.constant.CacheKeys;\nimport org.leo.im.http.file.AvatarStorage;\nimport org.leo.im.http.file.AvatarStorageFactory;\n\nimport java.io.RandomAccessFile;\nimport java.util.Iterator;\nimport java.util.Set;\n\nimport org.leo.im.api.dto.UserDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.web.annotation.GetMapping;\nimport org.leo.web.annotation.PatchMapping;\nimport org.leo.web.annotation.PathVariable;\nimport org.leo.web.annotation.PostMapping;\nimport org.leo.web.annotation.PutMapping;\nimport org.leo.web.annotation.RequestBody;\nimport org.leo.web.annotation.RequestHeader;\nimport org.leo.web.annotation.RequestMapping;\nimport org.leo.web.annotation.RequestParam;\nimport org.leo.web.annotation.RestController;\nimport org.leo.web.annotation.UploadFile;\nimport org.leo.web.multipart.MultipartFile;\nimport org.leo.web.rest.HttpStatus;\nimport org.leo.web.rest.ResponseEntity;\n\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.netty.handler.codec.http.FullHttpRequest;\nimport io.netty.handler.codec.http.cookie.Cookie;\nimport io.netty.handler.codec.http.cookie.ServerCookieDecoder;\n\n/**\n * 用户控制器\n * \n * @author Leo\n * @date 2018/4/9\n */\n@RestController()\n@RequestMapping(\"/users\")\npublic final class UserController extends BaseController {\n    \n    @GetMapping(\"/{id}\")\n    public ResponseEntity<UserDTO> getById(@PathVariable() String id) {\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        UserDTO dto = serviceProxy.getById(id);\n        return ResponseEntity.ok(dto);\n    }\n    \n    @GetMapping(\"/me\")\n    public ResponseEntity<UserDTO> getCurrentUser(@RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        UserDTO dto = serviceProxy.getById(userId);\n        return ResponseEntity.ok(dto);\n    }\n    \n    @PostMapping(\"\")\n    public ResponseEntity<?> register(FullHttpRequest request, @RequestBody String body) {\n        // 检查验证码\n        JSONObject json = JSONObject.parseObject(body);\n        String verificationCode = json.getString(\"verificationCode\");\n        if(verificationCode == null || verificationCode.trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"验证码为空\");\n        }\n        String sessionId = getJSessionId(request);\n        if(sessionId == null || sessionId.trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的请求\");\n        }\n        Object verificationCodeInCache = CacheManagerFactory.getCacheManager().get(CacheKeys.VERIFICATION_CODE_PREFIX + sessionId);\n        if(verificationCodeInCache == null || verificationCodeInCache.toString().trim().isEmpty()) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的验证码\");\n        }\n        if(!verificationCode.equals(verificationCodeInCache)) {\n            return ResponseEntity.status(HttpStatus.FORBIDDEN).build(\"无效的验证码\");\n        }\n        \n        // 注册用户\n        UserDTO dto = new UserDTO();\n        dto.setName(json.getString(\"name\"));\n        dto.setNickname(json.getString(\"nickname\"));\n        dto.setPassword(json.getString(\"password\"));\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        serviceProxy.saveUser(dto);\n        return ResponseEntity.created().build();\n    }\n    \n    @GetMapping(\"\")\n    public ResponseEntity<Page<UserDTO>> listUser(@RequestParam(\"name\") String name, @RequestParam(\"limit\") int limit,\n            @RequestParam(\"offset\") int offset) {\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        Page<UserDTO> result = serviceProxy.listByNameOrNickname(name, limit, offset);\n        return ResponseEntity.ok(result);\n    }\n    \n    @PatchMapping(\"/{id}\")\n    public ResponseEntity<UserDTO> patchUser(@PathVariable() String id, @RequestBody String body) {\n        JSONObject json = JSONObject.parseObject(body);\n        UserDTO dto = new UserDTO();\n        dto.setId(id);\n        if(json.containsKey(\"nickname\")) {\n            dto.setNickname(json.getString(\"nickname\"));\n        }\n        if(json.containsKey(\"password\")) {\n            dto.setPassword(json.getString(\"password\"));\n        }\n        if(json.containsKey(\"locked\")) {\n            dto.setLocked(json.getBoolean(\"locked\"));\n        }\n        if(json.containsKey(\"avatarUrl\") && !json.getString(\"avatarUrl\").trim().isEmpty()) {\n            dto.setAvatarUrl(json.getString(\"avatarUrl\"));\n        }\n        if(json.containsKey(\"lastPostAt\")) {\n            dto.setLastPostAt(json.getLong(\"lastPostAt\"));\n        }\n        if(json.containsKey(\"onlineStatus\")) {\n            dto.setOnlineStatus(json.getString(\"onlineStatus\"));\n        }\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        UserDTO returnDTO = serviceProxy.updateUser(dto, false);\n        return ResponseEntity.created(returnDTO);\n    }\n    \n    @PostMapping(\"/me/avatar\")\n    public ResponseEntity<String> uploadAvatar(@UploadFile MultipartFile avatar, @RequestHeader(\"X-Token\") String token) {\n        String userId = this.getSubjectFromJwt(token, \"userId\");\n        String imageType = \"png\";\n        if(\"image/jpeg\".equalsIgnoreCase(avatar.getFileType())) {\n            imageType = \"jpg\";\n        }\n        \n        // 生成两张图片，尺寸分别为 32x32、36x36、80x80\n        AvatarStorage as = AvatarStorageFactory.newInstance();\n        as.save(userId, imageType, avatar.getFileData(), 32, 32);\n        as.save(userId, imageType, avatar.getFileData(), 36, 36);\n        as.save(userId, imageType, avatar.getFileData(), 80, 80);\n        return ResponseEntity.created(\"avatar.\" + imageType);\n    }\n    \n    @GetMapping(\"/{id}/avatar\")\n    public ResponseEntity<RandomAccessFile> getMyAvatar(@PathVariable(\"id\") String userId, @RequestParam(\"width\") int width,\n            @RequestParam(\"height\") int height) {\n        // 得到用户头像\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        UserDTO dto = serviceProxy.getById(userId);\n        if(dto != null) {\n            String avatarUrl = dto.getAvatarUrl();\n            if(avatarUrl != null && !avatarUrl.trim().isEmpty()) {\n                AvatarStorage as = AvatarStorageFactory.newInstance();\n                RandomAccessFile raf = as.read(userId, avatarUrl, width, height);\n                String mimetype = avatarUrl.toLowerCase().trim().endsWith(\"png\") ? \"image/png\" : \"image/jpeg\";\n                return ResponseEntity.ok(raf, mimetype);\n            }\n            return ResponseEntity.noContent().build();\n        }\n        return ResponseEntity.notFound().build();\n    }\n    \n    @GetMapping(\"/nonChannelMembers\")\n    public ResponseEntity<Page<UserDTO>> listNonChannelMember(@RequestParam(\"channelId\") String channelId,\n            @RequestParam(\"username\") String username, @RequestParam(\"limit\") int limit, @RequestParam(\"offset\") int offset) {\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        Page<UserDTO> result = serviceProxy.listNonMembers(channelId, username, limit, offset);\n        return ResponseEntity.ok(result);\n    }\n    \n    @PutMapping(\"/{userId}/password\")\n    public ResponseEntity<Integer> changePassword(@PathVariable(\"userId\") String userId, @RequestHeader(\"X-Token\") String token, \n            @RequestBody String body) {\n        String currentUserId = this.getSubjectFromJwt(token, \"userId\");\n        if(!userId.equals(currentUserId)) {\n            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();\n        }\n        JSONObject data = JSONObject.parseObject(body);\n        UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n        int count = serviceProxy.updatePassword(userId, data.getString(\"username\"), data.getString(\"oldPassword\"), data.getString(\"newPassword\"));\n        return ResponseEntity.ok(count);\n    }\n    \n    /**\n     * 从cookie中得到Session Id\n     * @return\n     */\n    private String getJSessionId(FullHttpRequest request) {\n        try {\n            String cookieStr = request.headers().get(\"Cookie\");\n            if(cookieStr == null || cookieStr.trim().isEmpty()) {\n                return null;\n            }\n            Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieStr);\n            Iterator<Cookie> it = cookies.iterator();\n\n            while (it.hasNext()) {\n                Cookie cookie = it.next();\n                if (cookie.name().equals(CacheKeys.JSESSIONID)) {\n                    if (CacheManagerFactory.getCacheManager().get(cookie.value()) != null) {\n                        return cookie.value();\n                    }\n                }\n            }\n        } catch (Exception e1) {\n            return null;\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/exception/NoSuchSettingException.java",
    "content": "package org.leo.im.http.exception;\n\n/**\n * 无此配置异常类\n * \n * @author Leo\n * @date 2018/5/11\n */\npublic final class NoSuchSettingException extends RuntimeException {\n\n    private static final long serialVersionUID = 1911497055661761968L;\n    \n    public NoSuchSettingException() {\n    }\n\n    public NoSuchSettingException(String message) {\n        super(message);\n    }\n\n    public NoSuchSettingException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public NoSuchSettingException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/AbstractLocalFileStorage.java",
    "content": "package org.leo.im.http.file;\n\nimport java.io.FileNotFoundException;\nimport java.io.RandomAccessFile;\n\n/**\n * 本地文件存储抽象类\n * \n * @author Leo\n * @date 2018/5/12\n */\nabstract class AbstractLocalFileStorage {\n    \n    /**\n     * 读取文件内容\n     * @param fileName\n     * @return\n     */\n    public RandomAccessFile readFile(String fileName) {\n        try {\n            return new RandomAccessFile(fileName, \"r\");\n        } catch (FileNotFoundException ignore) {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/AvatarStorage.java",
    "content": "package org.leo.im.http.file;\n\nimport java.io.RandomAccessFile;\n\n/**\n * 头像存储接口\n * \n * @author Leo\n * @date 2018/5/11\n */\npublic interface AvatarStorage {\n    \n    /**\n     * 保存头像\n     * @param userId\n     * @param fileType\n     * @param data\n     * @param width\n     * @param height\n     * @return\n     */\n    boolean save(String userId, String fileType, byte[] data, int width, int height);\n    \n    /**\n     * 读取头像内容\n     * @param userId\n     * @param fileName\n     * @param width\n     * @param height\n     * @return\n     */\n    RandomAccessFile read(String userId, String fileName, int width, int height);\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/AvatarStorageFactory.java",
    "content": "package org.leo.im.http.file;\n\n/**\n * 头像存储工厂类\n * \n * @author Leo\n * @date 2018/5/11\n */\npublic final class AvatarStorageFactory {\n    \n    /**\n     * 创建头像存储类的实例\n     * @return\n     */\n    public static AvatarStorage newInstance() {\n        if(\"local\".equalsIgnoreCase(System.getProperty(\"file.settings.driver\"))) {\n            return new LocalAvatarStorage();\n        }\n        return new LocalAvatarStorage();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/FileStorage.java",
    "content": "package org.leo.im.http.file;\n\nimport java.io.RandomAccessFile;\n\n/**\n * 文件存储接口\n * \n * @author Leo\n * @date 2018/5/11\n */\npublic interface FileStorage {\n    \n    /**\n     * 保存文件\n     * @param key\n     * @param fileName\n     * @param data\n     * @return\n     */\n    boolean save(String key, String fileName, byte[] data);\n    \n    /**\n     * 保存预览图片\n     * @param key\n     * @param fileName\n     * @param data\n     * @param width\n     * @param height\n     * @return\n     */\n    short[] saveThumb(String key, String fileName, byte[] data, int width, int height);\n\n    /**\n     * 读取文件内容\n     * @param fileName\n     * @return\n     */\n    RandomAccessFile read(String fileName);\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/FileStorageFactory.java",
    "content": "package org.leo.im.http.file;\n\n/**\n * 文件存储工厂类\n * @author Administrator\n *\n */\npublic final class FileStorageFactory {\n\n    /**\n     * 得到文件存储类的实例\n     * @return\n     */\n    public static FileStorage newInstance() {\n        if(\"local\".equalsIgnoreCase(System.getProperty(\"file.settings.driver\"))) {\n            return new LocalFileStorage();\n        }\n        return new LocalFileStorage();\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/LocalAvatarStorage.java",
    "content": "package org.leo.im.http.file;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.RandomAccessFile;\n\nimport org.leo.im.http.exception.NoSuchSettingException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport net.coobird.thumbnailator.Thumbnails;\n\n/**\n * 本地存储头像实现类\n * \n * @author Leo\n * @date 2018/5/11\n */\nclass LocalAvatarStorage extends AbstractLocalFileStorage implements AvatarStorage {\n    \n    private static Logger logger = LoggerFactory.getLogger(LocalAvatarStorage.class);\n\n    /**\n     * 保存头像\n     * @param userId\n     * @param fileType\n     * @param data\n     * @param width\n     * @param height\n     * @return\n     */\n    @Override\n    public boolean save(String userId, String fileType, byte[] data, int width, int height) {\n        StringBuilder newFile = new StringBuilder(256);\n        newFile.append(System.getProperty(\"file.settings.directory\"));\n        if(newFile.length() == 0) {\n            throw new NoSuchSettingException(\"file.settings.directory\");\n        }\n        if(userId != null && !userId.trim().isEmpty()) {\n            newFile.append(\"/\").append(userId);\n        }\n        // 判断目录是否存在\n        File dir = new File(newFile.toString());\n        if(!dir.exists()) {\n            dir.mkdir();\n        }\n        \n        newFile.append(\"/avatar\").append(width).append(\"x\").append(height).append(\".\").append(fileType);\n        InputStream is = new ByteArrayInputStream(data);\n        try {\n            Thumbnails.of(is).size(width > 70 ? width : width * 2, height > 70 ? height : height * 2).toFile(newFile.toString());\n        } catch (IOException e) {\n            logger.error(e.getMessage());\n            return false;\n        }\n        return true;\n    }\n    \n    /**\n     * 读取头像内容\n     * @param userId\n     * @param fileName\n     * @param width\n     * @param height\n     * @return\n     */\n    @Override\n    public RandomAccessFile read(String userId, String fileName, int width, int height) {\n        StringBuilder file = new StringBuilder(256);\n        file.append(System.getProperty(\"file.settings.directory\"));\n        if(file.length() == 0) {\n            throw new NoSuchSettingException(\"file.settings.directory\");\n        }\n        if(userId != null && !userId.trim().isEmpty()) {\n            file.append(\"/\").append(userId);\n        }\n        String[] fileNameSplit = fileName.split(\"\\\\.\");\n        file.append(\"/avatar\").append(width).append(\"x\").append(height).append(\".\").append(fileNameSplit[fileNameSplit.length - 1]);\n        try {\n            return new RandomAccessFile(file.toString(), \"r\");\n        } catch (FileNotFoundException e) {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/file/LocalFileStorage.java",
    "content": "package org.leo.im.http.file;\n\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.RandomAccessFile;\n\nimport javax.imageio.ImageIO;\n\nimport org.leo.im.http.exception.NoSuchSettingException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport net.coobird.thumbnailator.Thumbnails;\n\n/**\n * 本地文件存储实现类\n * \n * @author Leo\n * @date 2018/5/11\n */\nclass LocalFileStorage extends AbstractLocalFileStorage implements FileStorage {\n    \n    private static Logger logger = LoggerFactory.getLogger(LocalFileStorage.class);\n\n    /**\n     * 保存文件\n     * @param key\n     * @param fileName\n     * @param data\n     */\n    @Override\n    public boolean save(String key, String fileName, byte[] data) {\n        StringBuilder newFile = new StringBuilder(256);\n        newFile.append(System.getProperty(\"file.settings.directory\"));\n        if(newFile.length() == 0) {\n            throw new NoSuchSettingException(\"file.settings.directory\");\n        }\n        if(key != null && !key.trim().isEmpty()) {\n            newFile.append(\"/\").append(key);\n        }\n        // 判断目录是否存在\n        File dir = new File(newFile.toString());\n        if(!dir.exists()) {\n            dir.mkdirs();\n        }\n        \n        // 创建文件\n        newFile.append(\"/\").append(fileName);\n        File file = new File(newFile.toString());\n        OutputStream os = null;\n        try {\n            file.createNewFile();\n            os = new FileOutputStream(file);\n            os.write(data);\n            return true;\n        } catch (IOException e) {\n            logger.error(e.getMessage());\n            return false;\n        } finally {\n            if(os != null) {\n                try {\n                    os.close();\n                } catch (IOException e) {\n                    logger.error(e.getMessage());\n                }\n            }\n        }\n    }\n    \n    /**\n     * 保存预览图片\n     * @param key\n     * @param fileName\n     * @param data\n     * @param width\n     * @param height\n     * @return\n     */\n    @Override\n    public short[] saveThumb(String key, String fileName, byte[] data, int width, int height) {\n        StringBuilder newFile = new StringBuilder(256);\n        newFile.append(System.getProperty(\"file.settings.directory\"));\n        if(newFile.length() == 0) {\n            throw new NoSuchSettingException(\"file.settings.directory\");\n        }\n        if(key != null && !key.trim().isEmpty()) {\n            newFile.append(\"/\").append(key);\n        }\n        // 判断目录是否存在\n        File dir = new File(newFile.toString());\n        if(!dir.exists()) {\n            dir.mkdir();\n        }\n        \n        newFile.append(\"/\").append(fileName);\n        InputStream is = new ByteArrayInputStream(data);\n        try {\n            Thumbnails.of(is).size(width, height).toFile(newFile.toString());\n            File picture = new File(newFile.toString());\n            BufferedImage sourceImage = ImageIO.read(new FileInputStream(picture));\n            return new short[] { (short)sourceImage.getWidth(), (short)sourceImage.getHeight() };\n        } catch (IOException e) {\n            logger.error(e.getMessage());\n            return null;\n        }\n    }\n\n    @Override\n    public RandomAccessFile read(String fileName) {\n        StringBuilder file = new StringBuilder(256);\n        file.append(System.getProperty(\"file.settings.directory\"));\n        if(file.length() == 0) {\n            throw new NoSuchSettingException(\"file.settings.directory\");\n        }\n        file.append(\"/\").append(fileName);\n        try {\n            return new RandomAccessFile(file.toString(), \"r\");\n        } catch (FileNotFoundException e) {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/interceptor/AuthenticationInterceptor.java",
    "content": "package org.leo.im.http.interceptor;\n\nimport org.leo.im.http.util.JwtUtils;\nimport org.leo.web.rest.HttpResponse;\nimport org.leo.web.rest.HttpStatus;\nimport org.leo.web.rest.interceptor.Interceptor;\n\nimport io.netty.handler.codec.http.FullHttpRequest;\n\n/**\n * 身份认证拦截器\n * \n * @author Leo\n * @date 2018/4/3\n */\npublic class AuthenticationInterceptor implements Interceptor {\n\n    @Override\n    public boolean preHandle(FullHttpRequest request, HttpResponse response) throws Exception {\n        if (request.method().name().equalsIgnoreCase(\"OPTIONS\")) {\n            return true;\n        }\n        try {\n            JwtUtils.parseJWT(request.headers().get(\"X-Token\"), System.getProperty(\"jwt.secret\"));\n            return true;\n        } catch (Exception e) {\n            response.write(HttpStatus.UNAUTHORIZED, \"Unauthorized\");\n            return false;\n        }\n    }\n\n    @Override\n    public void postHandle(FullHttpRequest request, HttpResponse response) throws Exception {\n    }\n\n    @Override\n    public void afterCompletion(FullHttpRequest request, HttpResponse response) {\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/interceptor/CorsInterceptor.java",
    "content": "package org.leo.im.http.interceptor;\n\nimport org.leo.web.rest.HttpResponse;\nimport org.leo.web.rest.interceptor.Interceptor;\n\nimport io.netty.handler.codec.http.FullHttpRequest;\n\n/**\n * 跨域拦截器\n * \n * @author Leo\n * @date 2018/4/2\n */\npublic final class CorsInterceptor implements Interceptor {\n\n    @Override\n    public boolean preHandle(FullHttpRequest request, HttpResponse response) throws Exception {\n        // 使用axios发送cookie，这里不能用*，需要使用Web前端地址，如：http://localhost:8080\n        // response.getHeaders().put(\"Access-Control-Allow-Origin\", \"*\");\n        response.getHeaders().put(\"Access-Control-Allow-Origin\", System.getProperty(\"http.origin\"));\n        response.getHeaders().put(\"Access-Control-Allow-Methods\", \"POST, PUT, GET, OPTIONS, DELETE, PATCH\");\n        response.getHeaders().put(\"Access-Control-Max-Age\", \"3600\");\n        response.getHeaders().put(\"Access-Control-Allow-Headers\", \"Content-Type,X-Token\");\n        response.getHeaders().put(\"Access-Control-Allow-Credentials\", \"true\");\n        return true;\n    }\n\n    @Override\n    public void postHandle(FullHttpRequest request, HttpResponse response) throws Exception {\n    }\n\n    @Override\n    public void afterCompletion(FullHttpRequest request, HttpResponse response) {\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/util/JwtUtils.java",
    "content": "package org.leo.im.http.util;\n\nimport java.util.Base64;\nimport java.util.Date;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.JwtBuilder;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\n\n/**\n * Jwt工具类\n * \n * @author Leo\n * @date 2018/3/28\n */\npublic class JwtUtils {\n\n    /**\n     * 生成加密key\n     * \n     * @param secret\n     * @return\n     */\n    private static SecretKey generalKey(String secret) {\n        byte[] encodedKey = Base64.getDecoder().decode(secret);\n        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, \"AES\");\n        return key;\n    }\n\n    /**\n     * 创建jwt\n     * \n     * @param subject\n     * @param secret\n     * @param ttlMillis\n     * @return\n     */\n    public static String createJWT(String subject, String secret, long ttlMillis) {\n        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;\n        long nowMillis = System.currentTimeMillis();\n        Date now = new Date(nowMillis);\n        SecretKey key = generalKey(secret);\n        JwtBuilder builder = Jwts.builder().setId(\"jwt\").setIssuedAt(now).setSubject(subject)\n                .signWith(signatureAlgorithm, key);\n        if (ttlMillis >= 0) {\n            long expMillis = nowMillis + ttlMillis;\n            Date exp = new Date(expMillis);\n            builder.setExpiration(exp);\n        }\n        return builder.compact();\n    }\n\n    /**\n     * 解密jwt\n     * \n     * @param jwt\n     * @param secret\n     * @return\n     * @throws Exception\n     */\n    public static Claims parseJWT(String jwt, String secret) {\n        SecretKey key = generalKey(secret);\n        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();\n        return claims;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/vo/ChannelListVO.java",
    "content": "package org.leo.im.http.vo;\n\n/**\n * 频道列表vo类\n * \n * @author Leo\n * @date 2018/4/3\n */\npublic final class ChannelListVO {\n\n    private String id;\n\n    private String name;\n    \n    private String displayName;\n\n    private String otherSideOnlineStatus;\n\n    private String type;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    public String getOtherSideOnlineStatus() {\n        return otherSideOnlineStatus;\n    }\n\n    public void setOtherSideOnlineStatus(String otherSideOnlineStatus) {\n        this.otherSideOnlineStatus = otherSideOnlineStatus;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChannelListVO [id=\" + id + \", name=\" + name + \", displayName=\" + displayName\n                + \", otherSideOnlineStatus=\" + otherSideOnlineStatus + \", type=\" + type + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/vo/ChannelVO.java",
    "content": "package org.leo.im.http.vo;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.ChannelMemberDTO;\n\n/**\n * 频道vo类\n * @author Leo\n * @date 2018/4/12\n */\npublic final class ChannelVO {\n    \n    private String id;\n\n    private String name;\n    \n    private String displayName;\n\n    private String type;\n\n    private int memberCount;\n    \n    private String otherSideId;\n    \n    private String otherSideOnlineStatus;\n    \n    private String creatorId;\n    \n    private List<ChannelMemberDTO> members = new ArrayList<>(128);\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public int getMemberCount() {\n        return memberCount;\n    }\n\n    public void setMemberCount(int memberCount) {\n        this.memberCount = memberCount;\n    }\n\n    public String getOtherSideId() {\n        return otherSideId;\n    }\n\n    public void setOtherSideId(String otherSideId) {\n        this.otherSideId = otherSideId;\n    }\n\n    public String getOtherSideOnlineStatus() {\n        return otherSideOnlineStatus;\n    }\n\n    public void setOtherSideOnlineStatus(String otherSideOnlineStatus) {\n        this.otherSideOnlineStatus = otherSideOnlineStatus;\n    }\n\n    public String getCreatorId() {\n        return creatorId;\n    }\n\n    public void setCreatorId(String creatorId) {\n        this.creatorId = creatorId;\n    }\n\n    public List<ChannelMemberDTO> getMembers() {\n        return members;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChannelVO [id=\" + id + \", name=\" + name + \", displayName=\" + displayName + \", type=\" + type\n                + \", memberCount=\" + memberCount + \", otherSideId=\" + otherSideId + \", otherSideOnlineStatus=\"\n                + otherSideOnlineStatus + \", creatorId=\" + creatorId + \", members=\" + members + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/vo/MessageVO.java",
    "content": "package org.leo.im.http.vo;\n\n/**\n * 消息vo类\n * \n * @author Leo\n * @date 2018/5/16\n */\npublic final class MessageVO {\n\n    private long id;\n    \n    private String channelId;\n\n    private long createAt;\n\n    private String type;\n\n    private String senderId;\n\n    private String senderName;\n\n    private String senderNickname;\n\n    private String senderOnlineStatus;\n\n    private String senderAvatarUrl;\n    \n    private String senderFirstLetterOfName;\n    \n    private String content;\n    \n    private String fileName;\n    \n    private String fileExtension;\n    \n    private int fileSize;\n    \n    private String fileMimeType;\n    \n    private String fileThumbPath;\n    \n    private String filePath;  \n    \n    private int imageWidth;\n    \n    private int imageHeight;\n    \n    private short imageThumbWidth;\n    \n    private short imageThumbHeight;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(String channelId) {\n        this.channelId = channelId;\n    }\n\n    public long getCreateAt() {\n        return createAt;\n    }\n\n    public void setCreateAt(long createAt) {\n        this.createAt = createAt;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getSenderId() {\n        return senderId;\n    }\n\n    public void setSenderId(String senderId) {\n        this.senderId = senderId;\n    }\n\n    public String getSenderName() {\n        return senderName;\n    }\n\n    public void setSenderName(String senderName) {\n        this.senderName = senderName;\n    }\n\n    public String getSenderNickname() {\n        return senderNickname;\n    }\n\n    public void setSenderNickname(String senderNickname) {\n        this.senderNickname = senderNickname;\n    }\n\n    public String getSenderOnlineStatus() {\n        return senderOnlineStatus;\n    }\n\n    public void setSenderOnlineStatus(String senderOnlineStatus) {\n        this.senderOnlineStatus = senderOnlineStatus;\n    }\n\n    public String getSenderAvatarUrl() {\n        return senderAvatarUrl;\n    }\n\n    public void setSenderAvatarUrl(String senderAvatarUrl) {\n        this.senderAvatarUrl = senderAvatarUrl;\n    }\n\n    public String getSenderFirstLetterOfName() {\n        return senderFirstLetterOfName;\n    }\n\n    public void setSenderFirstLetterOfName(String senderFirstLetterOfName) {\n        this.senderFirstLetterOfName = senderFirstLetterOfName;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(String content) {\n        this.content = content;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public void setFileName(String fileName) {\n        this.fileName = fileName;\n    }\n\n    public String getFileExtension() {\n        return fileExtension;\n    }\n\n    public void setFileExtension(String fileExtension) {\n        this.fileExtension = fileExtension;\n    }\n\n    public int getFileSize() {\n        return fileSize;\n    }\n\n    public void setFileSize(int fileSize) {\n        this.fileSize = fileSize;\n    }\n\n    public String getFileMimeType() {\n        return fileMimeType;\n    }\n\n    public void setFileMimeType(String fileMimeType) {\n        this.fileMimeType = fileMimeType;\n    }\n\n    public String getFileThumbPath() {\n        return fileThumbPath;\n    }\n\n    public void setFileThumbPath(String fileThumbPath) {\n        this.fileThumbPath = fileThumbPath;\n    }\n\n    public String getFilePath() {\n        return filePath;\n    }\n\n    public void setFilePath(String filePath) {\n        this.filePath = filePath;\n    }\n    \n    public int getImageWidth() {\n        return imageWidth;\n    }\n\n    public void setImageWidth(int imageWidth) {\n        this.imageWidth = imageWidth;\n    }\n\n    public int getImageHeight() {\n        return imageHeight;\n    }\n\n    public void setImageHeight(int imageHeight) {\n        this.imageHeight = imageHeight;\n    }\n\n    public short getImageThumbWidth() {\n        return imageThumbWidth;\n    }\n\n    public void setImageThumbWidth(short imageThumbWidth) {\n        this.imageThumbWidth = imageThumbWidth;\n    }\n\n    public short getImageThumbHeight() {\n        return imageThumbHeight;\n    }\n\n    public void setImageThumbHeight(short imageThumbHeight) {\n        this.imageThumbHeight = imageThumbHeight;\n    }\n\n    public String getSenderRealAvatarUrl() {\n        if(\"http://\".equalsIgnoreCase(this.senderAvatarUrl) || \"https://\".equalsIgnoreCase(this.senderAvatarUrl)) {\n            return this.senderAvatarUrl;\n        }\n        if(this.senderAvatarUrl != null && !this.senderAvatarUrl.trim().isEmpty()) {\n            return this.senderAvatarUrl;\n        }\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return \"MessageVO [id=\" + id + \", channelId=\" + channelId + \", createAt=\" + createAt + \", type=\" + type\n                + \", senderId=\" + senderId + \", senderName=\" + senderName + \", senderNickname=\" + senderNickname\n                + \", senderOnlineStatus=\" + senderOnlineStatus + \", senderAvatarUrl=\" + senderAvatarUrl\n                + \", senderFirstLetterOfName=\" + senderFirstLetterOfName + \", content=\" + content + \", fileName=\"\n                + fileName + \", fileExtension=\" + fileExtension + \", fileSize=\" + fileSize + \", fileMimeType=\"\n                + fileMimeType + \", fileThumbPath=\" + fileThumbPath + \", filePath=\" + filePath + \", imageWidth=\"\n                + imageWidth + \", imageHeight=\" + imageHeight + \", imageThumbWidth=\" + imageThumbWidth\n                + \", imageThumbHeight=\" + imageThumbHeight + \"]\";\n    }\n \n}\n"
  },
  {
    "path": "leo-im-http/src/main/java/org/leo/im/http/vo/UserChannelVO.java",
    "content": "package org.leo.im.http.vo;\n\n/**\n * 用户频道vo类\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic final class UserChannelVO {\n    \n    private String channelId;\n    \n    private String channelName;\n    \n    private String channelType;\n    \n    private String channelDisplayName;\n    \n    private String channelDescription;\n    \n    private String toUserId;\n    \n    private String toUserOnlineStatus;\n    \n    private short unreadMessageCount;\n    \n    private int memberCount;\n    \n    private String creatorId;\n\n    public String getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(String channelId) {\n        this.channelId = channelId;\n    }\n\n    public String getChannelName() {\n        return channelName;\n    }\n\n    public void setChannelName(String channelName) {\n        this.channelName = channelName;\n    }\n\n    public String getChannelType() {\n        return channelType;\n    }\n\n    public void setChannelType(String channelType) {\n        this.channelType = channelType;\n    }\n\n    public String getChannelDisplayName() {\n        return channelDisplayName;\n    }\n\n    public void setChannelDisplayName(String channelDisplayName) {\n        this.channelDisplayName = channelDisplayName;\n    }\n\n    public String getChannelDescription() {\n        return channelDescription;\n    }\n\n    public void setChannelDescription(String channelDescription) {\n        this.channelDescription = channelDescription;\n    }\n\n    public String getToUserId() {\n        return toUserId;\n    }\n\n    public void setToUserId(String toUserId) {\n        this.toUserId = toUserId;\n    }\n\n    public String getToUserOnlineStatus() {\n        return toUserOnlineStatus;\n    }\n\n    public void setToUserOnlineStatus(String toUserOnlineStatus) {\n        this.toUserOnlineStatus = toUserOnlineStatus;\n    }\n\n    public short getUnreadMessageCount() {\n        return unreadMessageCount;\n    }\n\n    public void setUnreadMessageCount(short unreadMessageCount) {\n        this.unreadMessageCount = unreadMessageCount;\n    }\n\n    public int getMemberCount() {\n        return memberCount;\n    }\n\n    public void setMemberCount(int memberCount) {\n        this.memberCount = memberCount;\n    }\n\n    public String getCreatorId() {\n        return creatorId;\n    }\n\n    public void setCreatorId(String creatorId) {\n        this.creatorId = creatorId;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserChannelVO [channelId=\" + channelId + \", channelName=\" + channelName + \", channelType=\" + channelType\n                + \", channelDisplayName=\" + channelDisplayName + \", channelDescription=\" + channelDescription\n                + \", toUserId=\" + toUserId + \", toUserOnlineStatus=\" + toUserOnlineStatus + \", unreadMessageCount=\"\n                + unreadMessageCount + \", memberCount=\" + memberCount + \", creatorId=\" + creatorId + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-http/src/test/java/org/leo/im/http/AppTest.java",
    "content": "package org.leo.im.http;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-http/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: asm-6.1.jar thumbnailator-0.4.8.jar netty-all-4.1.24.Final\n .jar fastjson-1.2.47.jar netty-rest-server-1.0.jar leo-im-api-1.0.jar\n  leo-im-api-provider-1.0.jar leo-im-service-1.0.jar leo-im-store-1.0.\n jar mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1\n .9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar cglib-3.2.6.jar ant-1\n .9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar jackson-databind-2.8.9\n .jar jackson-annotations-2.8.0.jar jackson-core-2.8.9.jar leo-im-noti\n fication-1.0.jar jedis-2.9.0.jar commons-pool2-2.4.2.jar leo-im-commo\n n-1.0.jar slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-core\n -1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-http/target/classes/META-INF/maven/org.leo.im/leo-im-http/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:34 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-http\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-http\nartifactId=leo-im-http\n"
  },
  {
    "path": "leo-im-http/target/classes/META-INF/maven/org.leo.im/leo-im-http/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-http</artifactId>\n\t<name>leo-im-http</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.ow2.asm</groupId>\n\t\t\t<artifactId>asm</artifactId>\n\t\t\t<version>6.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>net.coobird</groupId>\n\t\t\t<artifactId>thumbnailator</artifactId>\n\t\t\t<version>0.4.8</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>io.netty</groupId>\n\t\t\t<artifactId>netty-all</artifactId>\n\t\t\t<version>${netty.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>${fastjson.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo</groupId>\n\t\t\t<artifactId>netty-rest-server</artifactId>\n\t\t\t<version>1.0</version>\n\t\t\t<!-- \n\t\t\t<scope>system</scope>\n\t\t\t<systemPath>${project.basedir}/lib/netty-rest-server-1.0.jar</systemPath>\n\t\t\t -->\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api-provider</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-notification</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-http/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:21 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-http\n"
  },
  {
    "path": "leo-im-http/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-http/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\BaseController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\interceptor\\AuthenticationInterceptor.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\ChannelController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\cache\\CacheManagerFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\HttpServer.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\interceptor\\CorsInterceptor.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\vo\\ChannelVO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\cache\\CacheManager.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\AuthController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\vo\\UserChannelVO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\exception\\NoSuchSettingException.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\LocalAvatarStorage.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\cache\\MapCacheManager.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\LocalFileStorage.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\ExceptionController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\AvatarStorage.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\cache\\Cache.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\util\\JwtUtils.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\vo\\ChannelListVO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\FileStorage.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\MessageController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\UserChannelController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\vo\\MessageVO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\AvatarStorageFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\controller\\UserController.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\AbstractLocalFileStorage.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\file\\FileStorageFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\main\\java\\org\\leo\\im\\http\\constant\\CacheKeys.java\n"
  },
  {
    "path": "leo-im-http/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-http/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-http\\src\\test\\java\\org\\leo\\im\\http\\AppTest.java\n"
  },
  {
    "path": "leo-im-http/target/surefire-reports/TEST-org.leo.im.http.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.http.AppTest\" time=\"0.002\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.http.AppTest\" name=\"testApp\" time=\"0.002\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-http/target/surefire-reports/org.leo.im.http.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.http.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.065 sec\n"
  },
  {
    "path": "leo-im-migration/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry excluding=\"**\" kind=\"src\" output=\"target/classes\" path=\"src/main/resources\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-migration/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-migration</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-migration/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/main/resources=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-migration/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-migration/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-migration/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-migration</artifactId>\n\t<name>leo-im-migration</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.flywaydb</groupId>\n\t\t\t<artifactId>flyway-core</artifactId>\n\t\t\t<version>${flyway-core.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-migration/src/main/java/org/leo/im/migration/FlywayMigration.java",
    "content": "package org.leo.im.migration;\n\nimport org.flywaydb.core.Flyway;\n\npublic class FlywayMigration {\n\t\n\t/**\n\t * 数据库迁移\n\t * @param url JDBC URL\n\t * @param user 数据库用户\n\t * @param password 数据库口令\n\t */\n\tpublic void migrate(String url, String user, String password) {\n\t\tFlyway flyway = new Flyway();\n\t\tflyway.setDataSource(url, user, password);\n\t\tflyway.migrate();\n\t}\n\n}\n"
  },
  {
    "path": "leo-im-migration/src/main/resources/db/migration/V20180615___Init.sql",
    "content": "CREATE TABLE IF NOT EXISTS `im_channel` (\n  `id` char(32) NOT NULL,\n  `name` varchar(64) NOT NULL,\n  `type` char(1) NOT NULL,\n  `purpose` varchar(256) DEFAULT NULL,\n  `create_at` bigint(20) unsigned NOT NULL,\n  `delete_at` bigint(20) unsigned NOT NULL DEFAULT '0',\n  `last_post_at` bigint(20) unsigned NOT NULL DEFAULT '0',\n  `member_count` mediumint(8) unsigned NOT NULL DEFAULT '0',\n  `creator_id` char(32) NOT NULL DEFAULT '0',\n  `from_user_id` char(32) DEFAULT '0',\n  `to_user_id` char(32) DEFAULT '0',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_channel_member` (\n  `channel_id` char(32) NOT NULL,\n  `user_id` char(32) NOT NULL,\n  `is_admin` tinyint(3) unsigned NOT NULL,\n  PRIMARY KEY (`channel_id`,`user_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_file` (\n  `id` char(32) NOT NULL,\n  `name` varchar(128) NOT NULL,\n  `extension` varchar(32) NOT NULL,\n  `size` int(11) NOT NULL,\n  `mime_typ` varchar(256) NOT NULL,\n  `width` smallint(6) NOT NULL,\n  `height` smallint(6) NOT NULL,\n  `path` varchar(128) NOT NULL,\n  `thumb_width` smallint(6) DEFAULT NULL,\n  `thumb_height` smallint(6) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_hide_channel` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_message` (\n  `id` bigint(20) unsigned NOT NULL,\n  `channel_id` char(32) COLLATE utf8mb4_bin NOT NULL,\n  `sender_id` char(32) COLLATE utf8mb4_bin NOT NULL,\n  `create_at` bigint(20) NOT NULL,\n  `type` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,\n  `content` varchar(3000) COLLATE utf8mb4_bin DEFAULT NULL,\n  `delete_at` bigint(20) NOT NULL DEFAULT '0',\n  `file_id` char(32) COLLATE utf8mb4_bin DEFAULT '0',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\nCREATE TABLE IF NOT EXISTS `im_unread_message_count` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  `total` smallint(5) unsigned NOT NULL DEFAULT '0',\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_user` (\n  `id` char(32) NOT NULL,\n  `name` varchar(32) NOT NULL,\n  `name_first_letter` char(1) NOT NULL,\n  `nickname` varchar(32) NOT NULL,\n  `salt` varchar(64) NOT NULL,\n  `password` varchar(64) NOT NULL,\n  `locked` tinyint(4) NOT NULL DEFAULT '0',\n  `avatar_url` varchar(512) DEFAULT NULL,\n  `created_at` datetime NOT NULL,\n  `last_post_at` bigint(20) DEFAULT NULL,\n  `online_status` varchar(7) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_user_channel` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  `display_name` varchar(64) NOT NULL,\n  `to_user_id` char(32) DEFAULT NULL,\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `im_user` (`id`, `name`, `name_first_letter`, `nickname`, `salt`, `password`, `locked`, `avatar_url`, `created_at`, `last_post_at`, `online_status`) VALUES\n    ('00000000000000000000000000000000', 'leo', 'l', '系统用户', 'MOlssyhqweLKffidserewr==', 'FDFGHTY33456FDHG000FDEKKKLLLPP', 0, NULL, '2018-06-16 21:00:00', NULL, NULL);\n"
  },
  {
    "path": "leo-im-migration/src/test/java/org/leo/im/migration/AppTest.java",
    "content": "package org.leo.im.migration;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-migration/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: flyway-core-5.1.1.jar slf4j-api-1.7.25.jar logback-classic\n -1.2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-migration/target/classes/META-INF/maven/org.leo.im/leo-im-migration/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:09 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-migration\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-migration\nartifactId=leo-im-migration\n"
  },
  {
    "path": "leo-im-migration/target/classes/META-INF/maven/org.leo.im/leo-im-migration/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-migration</artifactId>\n\t<name>leo-im-migration</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.flywaydb</groupId>\n\t\t\t<artifactId>flyway-core</artifactId>\n\t\t\t<version>${flyway-core.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-migration/target/classes/db/migration/V20180615___Init.sql",
    "content": "CREATE TABLE IF NOT EXISTS `im_channel` (\n  `id` char(32) NOT NULL,\n  `name` varchar(64) NOT NULL,\n  `type` char(1) NOT NULL,\n  `purpose` varchar(256) DEFAULT NULL,\n  `create_at` bigint(20) unsigned NOT NULL,\n  `delete_at` bigint(20) unsigned NOT NULL DEFAULT '0',\n  `last_post_at` bigint(20) unsigned NOT NULL DEFAULT '0',\n  `member_count` mediumint(8) unsigned NOT NULL DEFAULT '0',\n  `creator_id` char(32) NOT NULL DEFAULT '0',\n  `from_user_id` char(32) DEFAULT '0',\n  `to_user_id` char(32) DEFAULT '0',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_channel_member` (\n  `channel_id` char(32) NOT NULL,\n  `user_id` char(32) NOT NULL,\n  `is_admin` tinyint(3) unsigned NOT NULL,\n  PRIMARY KEY (`channel_id`,`user_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_file` (\n  `id` char(32) NOT NULL,\n  `name` varchar(128) NOT NULL,\n  `extension` varchar(32) NOT NULL,\n  `size` int(11) NOT NULL,\n  `mime_typ` varchar(256) NOT NULL,\n  `width` smallint(6) NOT NULL,\n  `height` smallint(6) NOT NULL,\n  `path` varchar(128) NOT NULL,\n  `thumb_width` smallint(6) DEFAULT NULL,\n  `thumb_height` smallint(6) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_hide_channel` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_message` (\n  `id` bigint(20) unsigned NOT NULL,\n  `channel_id` char(32) COLLATE utf8mb4_bin NOT NULL,\n  `sender_id` char(32) COLLATE utf8mb4_bin NOT NULL,\n  `create_at` bigint(20) NOT NULL,\n  `type` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL,\n  `content` varchar(3000) COLLATE utf8mb4_bin DEFAULT NULL,\n  `delete_at` bigint(20) NOT NULL DEFAULT '0',\n  `file_id` char(32) COLLATE utf8mb4_bin DEFAULT '0',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\nCREATE TABLE IF NOT EXISTS `im_unread_message_count` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  `total` smallint(5) unsigned NOT NULL DEFAULT '0',\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_user` (\n  `id` char(32) NOT NULL,\n  `name` varchar(32) NOT NULL,\n  `name_first_letter` char(1) NOT NULL,\n  `nickname` varchar(32) NOT NULL,\n  `salt` varchar(64) NOT NULL,\n  `password` varchar(64) NOT NULL,\n  `locked` tinyint(4) NOT NULL DEFAULT '0',\n  `avatar_url` varchar(512) DEFAULT NULL,\n  `created_at` datetime NOT NULL,\n  `last_post_at` bigint(20) DEFAULT NULL,\n  `online_status` varchar(7) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `im_user_channel` (\n  `user_id` char(32) NOT NULL,\n  `channel_id` char(32) NOT NULL,\n  `display_name` varchar(64) NOT NULL,\n  `to_user_id` char(32) DEFAULT NULL,\n  PRIMARY KEY (`user_id`,`channel_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nINSERT INTO `im_user` (`id`, `name`, `name_first_letter`, `nickname`, `salt`, `password`, `locked`, `avatar_url`, `created_at`, `last_post_at`, `online_status`) VALUES\n    ('00000000000000000000000000000000', 'leo', 'l', '系统用户', 'MOlssyhqweLKffidserewr==', 'FDFGHTY33456FDHG000FDEKKKLLLPP', 0, NULL, '2018-06-16 21:00:00', NULL, NULL);\n"
  },
  {
    "path": "leo-im-migration/target/maven-archiver/pom.properties",
    "content": "#Created by Apache Maven 3.3.9\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-migration\n"
  },
  {
    "path": "leo-im-migration/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-migration/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-migration\\src\\main\\java\\org\\leo\\im\\migration\\FlywayMigration.java\n"
  },
  {
    "path": "leo-im-model/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-model/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-model</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-model/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-model/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-model/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-model/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-model</artifactId>\n\t<name>leo-im-model</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/Channel.java",
    "content": "package org.leo.im.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 频道模型类\n * \n * @author Leo\n * @date 2018/4/7\n */\npublic final class Channel {\n\n    private String id;\n    \n    private String name;\n    \n    private User from;\n    \n    private User to;\n    \n    /**\n     * 频道类型\n     * G：群聊；P：私聊\n     */\n    private String type;\n    \n    private String purpose;\n    \n    private long createAt;\n    \n    private long deleteAt;\n    \n    private long lastPostAt;\n    \n    private int memberCount;\n    \n    private User creator;\n    \n    private List<ChannelMember> members = new ArrayList<>(128);\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public User getFrom() {\n        return from;\n    }\n\n    public void setFrom(User from) {\n        this.from = from;\n    }\n\n    public User getTo() {\n        return to;\n    }\n\n    public void setTo(User to) {\n        this.to = to;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getPurpose() {\n        return purpose;\n    }\n\n    public void setPurpose(String purpose) {\n        this.purpose = purpose;\n    }\n\n    public long getCreateAt() {\n        return createAt;\n    }\n\n    public void setCreateAt(long createAt) {\n        this.createAt = createAt;\n    }\n\n    public long getDeleteAt() {\n        return deleteAt;\n    }\n\n    public void setDeleteAt(long deleteAt) {\n        this.deleteAt = deleteAt;\n    }\n\n    public long getLastPostAt() {\n        return lastPostAt;\n    }\n\n    public void setLastPostAt(long lastPostAt) {\n        this.lastPostAt = lastPostAt;\n    }\n\n    public int getMemberCount() {\n        return memberCount;\n    }\n\n    public void setMemberCount(int memberCount) {\n        this.memberCount = memberCount;\n    }\n\n    public User getCreator() {\n        return creator;\n    }\n\n    public void setCreator(User creator) {\n        this.creator = creator;\n    }\n\n    public List<ChannelMember> getMembers() {\n        return members;\n    }\n\n    @Override\n    public String toString() {\n        return \"Channel [id=\" + id + \", name=\" + name + \", from=\" + from + \", to=\" + to + \", type=\" + type\n                + \", purpose=\" + purpose + \", createAt=\" + createAt + \", deleteAt=\" + deleteAt + \", lastPostAt=\"\n                + lastPostAt + \", memberCount=\" + memberCount + \", creator=\" + creator + \", members=\" + members + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/ChannelMember.java",
    "content": "package org.leo.im.model;\n\n/**\n * 频道成员实体类\n * \n * @author Leo\n * @date 2018/4/11\n */\npublic final class ChannelMember {\n    \n    private User user;\n    \n    private boolean admin;\n    \n    public User getUser() {\n        return this.user;\n    }\n    public void setUser(User user) {\n        this.user = user;\n    }\n    \n    public boolean getAdmin() {\n        return this.admin;\n    }\n    public void setAdmin(boolean admin) {\n        this.admin = admin;\n    }\n    \n    @Override\n    public String toString() {\n        return \"ChannelMember [user=\" + user + \", admin=\" + admin + \"]\";\n    }\n\n\n}\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/File.java",
    "content": "package org.leo.im.model;\n\n/**\n * 文件类\n * \n * @author Leo\n * @date 2018/6/13\n */\npublic class File {\n    \n    private String id;\n    \n    private String name;\n    \n    private String extension;\n    \n    private int size;\n    \n    private String mimeType;\n    \n    private int width;\n    \n    private int height;\n    \n    private short thumbWidth;\n    \n    private short thumbHeight;\n    \n    private String path;\n\n    public String getId() {\n        return id;\n    }\n\n    public void setId(String id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getExtension() {\n        return extension;\n    }\n\n    public void setExtension(String extension) {\n        this.extension = extension;\n    }\n\n    public int getSize() {\n        return size;\n    }\n\n    public void setSize(int size) {\n        this.size = size;\n    }\n\n    public String getMimeType() {\n        return mimeType;\n    }\n\n    public void setMimeType(String mimeType) {\n        this.mimeType = mimeType;\n    }\n\n    public int getWidth() {\n        return width;\n    }\n\n    public void setWidth(int width) {\n        this.width = width;\n    }\n\n    public int getHeight() {\n        return height;\n    }\n\n    public void setHeight(int height) {\n        this.height = height;\n    }\n\n    public short getThumbWidth() {\n        return thumbWidth;\n    }\n\n    public void setThumbWidth(short thumbWidth) {\n        this.thumbWidth = thumbWidth;\n    }\n\n    public short getThumbHeight() {\n        return thumbHeight;\n    }\n\n    public void setThumbHeight(short thumbHeight) {\n        this.thumbHeight = thumbHeight;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    @Override\n    public String toString() {\n        return \"File [id=\" + id + \", name=\" + name + \", extension=\" + extension + \", size=\" + size + \", mimeType=\"\n                + mimeType + \", width=\" + width + \", height=\" + height + \", thumbWidth=\" + thumbWidth + \", thumbHeight=\"\n                + thumbHeight + \", path=\" + path + \"]\";\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/Message.java",
    "content": "package org.leo.im.model;\n\n/**\n * 消息类\n * \n * @author Leo\n * @date 2018/4/21\n */\npublic final class Message {\n    \n    private long id;\n    \n    private Channel channel;\n    \n    private User sender;\n    \n    private long createAt;\n    \n    /**\n     * 消息类型（如：加某某加入群组、某某退出群组）\n     */\n    private String type;\n    \n    private String content;\n    \n    private File file;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n\n    public User getSender() {\n        return sender;\n    }\n\n    public void setSender(User sender) {\n        this.sender = sender;\n    }\n\n    public long getCreateAt() {\n        return createAt;\n    }\n\n    public void setCreateAt(long createAt) {\n        this.createAt = createAt;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(String content) {\n        this.content = content;\n    }\n\n    public File getFile() {\n        return file;\n    }\n\n    public void setFile(File file) {\n        this.file = file;\n    }\n\n    @Override\n    public String toString() {\n        return \"Message [id=\" + id + \", channel=\" + channel + \", sender=\" + sender + \", createAt=\" + createAt\n                + \", type=\" + type + \", content=\" + content + \", file=\" + file + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/User.java",
    "content": "package org.leo.im.model;\n\nimport java.util.Date;\n\n/**\n * 用户模型类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic final class User {\n    \n    private String id;\n    \n    private String name;\n    \n    private String firstLetterOfName;\n    \n    private String nickname;\n    \n    private String salt;\n    \n    private String password;\n    \n    private Boolean locked;\n    \n    private Date createdAt;\n    \n    private String avatarUr;\n    \n    private long lastPostAt;\n    \n    private String onlineStatus;\n    \n    public String getId() {\n        return this.id;\n    }\n    public void setId(String id) {\n        this.id = id;\n    }\n    \n    public String getName() {\n        return this.name;\n    }\n    public void setName(String name) {\n        this.name = name;\n    }\n    \n    public String getFirstLetterOfName() {\n        return this.firstLetterOfName;\n    }\n    public void setFirstLetterOfName(String firstLetter) {\n        this.firstLetterOfName = firstLetter;\n    }\n    \n    public String getNickname() {\n        return this.nickname;\n    }\n    public void setNickname(String nickname) {\n        this.nickname = nickname;\n    }\n    \n    public String getSalt() {\n        return this.salt;\n    }\n    public void setSalt(String salt) {\n        this.salt = salt;\n    }\n    \n    public String getPassword() {\n        return this.password;\n    }\n    public void setPassword(String password) {\n        this.password = password;\n    }\n    \n    public Boolean getLocked() {\n        return this.locked;\n    }\n    public void setLocked(Boolean locked) {\n        this.locked = locked;\n    }\n    \n    public Date getCreatedAt() {\n        return this.createdAt;\n    }\n    public void setCreatedAt(Date createdAt) {\n        this.createdAt = createdAt;\n    }\n    \n    public long getLastPostAt() {\n        return this.lastPostAt;\n    }\n    public void setLastPostAt(long lastPostAt) {\n        this.lastPostAt = lastPostAt;\n    }\n    \n    public String getAvatarUrl() {\n        return this.avatarUr;\n    }\n    public void setAvatarUrl(String avatarUr) {\n        this.avatarUr = avatarUr;\n    }\n    \n    public String getOnlineStatus() {\n        return this.onlineStatus;\n    }\n    public void setOnlineStatus(String onlineStatus) {\n        this.onlineStatus = onlineStatus;\n    }\n    \n    @Override\n    public String toString() {\n        return \"User [id=\" + id + \", name=\" + name + \", firstLetterOfName=\" + firstLetterOfName + \", nickname=\"\n                + nickname + \", salt=\" + salt + \", password=\" + password + \", locked=\" + locked + \", createdAt=\"\n                + createdAt + \", avatarUr=\" + avatarUr + \", onlineStatus=\" + onlineStatus + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-model/src/main/java/org/leo/im/model/UserChannel.java",
    "content": "package org.leo.im.model;\n\n/**\n * 用户频道类\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic final class UserChannel {\n    \n    private User user;\n    \n    private Channel channel;\n    \n    private String displayName;\n    \n    private User toUser;\n    \n    private short unreadMessageCount;\n\n    public User getUser() {\n        return user;\n    }\n\n    public void setUser(User user) {\n        this.user = user;\n    }\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n\n    public String getDisplayName() {\n        return displayName;\n    }\n\n    public void setDisplayName(String displayName) {\n        this.displayName = displayName;\n    }\n\n    public User getToUser() {\n        return toUser;\n    }\n\n    public void setToUser(User toUser) {\n        this.toUser = toUser;\n    }\n\n    public short getUnreadMessageCount() {\n        return unreadMessageCount;\n    }\n\n    public void setUnreadMessageCount(short unreadMessageCount) {\n        this.unreadMessageCount = unreadMessageCount;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserChannel [user=\" + user + \", channel=\" + channel + \", displayName=\" + displayName + \", toUser=\"\n                + toUser + \", unreadMessageCount=\" + unreadMessageCount + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "leo-im-model/src/test/java/org/leo/im/model/AppTest.java",
    "content": "package org.leo.im.model;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-model/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-cor\n e-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-model/target/classes/META-INF/maven/org.leo.im/leo-im-model/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:10 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-model\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-model\nartifactId=leo-im-model\n"
  },
  {
    "path": "leo-im-model/target/classes/META-INF/maven/org.leo.im/leo-im-model/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-model</artifactId>\n\t<name>leo-im-model</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-model/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:10 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-model\n"
  },
  {
    "path": "leo-im-model/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-model/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\ChannelMember.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\User.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\Channel.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\UserChannel.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\File.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\main\\java\\org\\leo\\im\\model\\Message.java\n"
  },
  {
    "path": "leo-im-model/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-model/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-model\\src\\test\\java\\org\\leo\\im\\model\\AppTest.java\n"
  },
  {
    "path": "leo-im-model/target/surefire-reports/TEST-org.leo.im.model.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.model.AppTest\" time=\"0.001\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.model.AppTest\" name=\"testApp\" time=\"0.001\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-model/target/surefire-reports/org.leo.im.model.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.model.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 sec\n"
  },
  {
    "path": "leo-im-notification/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-notification/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-notification</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-notification/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-notification/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-notification/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-notification/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-notification</artifactId>\n\t<name>leo-im-notification</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>redis.clients</groupId>\n\t\t\t<artifactId>jedis</artifactId>\n\t\t\t<version>${jedis.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>${fastjson.version}</version>\n        </dependency>\t\t\t\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/ActionNames.java",
    "content": "package org.leo.im.notification;\n\n/**\n * 动作名称常量类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class ActionNames {\n    \n    /**\n     * 在线状态改变\n     */\n    public static final String ONLINE_STATUS_CHANGED = \"ONLINE_STATUS_CHANGED\";\n    \n    /**\n     * 昵称改变\n     */\n    public static final String NICKNAME_CHANGED = \"NICKNAME_CHANGED\";\n    \n    /**\n     * 头像改变\n     */\n    public static final String AVATAR_CHANGED = \"AVATAR_CHANGED\";\n    \n    /**\n     * 收到新消息\n     */\n    public static final String NEW_MESSAGE = \"NEW_MESSAGE\";\n    \n    /**\n     * 读取消息\n     */\n    public static final String READ_MESSAGE = \"READ_MESSAGE\";\n    \n    /**\n     * 创建频道\n     */\n    public static final String CREATE_CHANNEL = \"CREATE_CHANNEL\";\n    \n    /**\n     * 频道被删除\n     */\n    public static final String CHANNEL_REMOVED = \"CHANNEL_REMOVED\";\n    \n    /**\n     * 加入频道\n     */\n    public static final String JOIN_CHANNEL = \"JOIN_CHANNEL\";\n    \n    /**\n     * 离开频道\n     */\n    public static final String LEAVE_CHANNEL = \"LEAVE_CHANNEL\";\n    \n    /**\n     * 被移除频道\n     */\n    public static final String REMOVE_FROM_CHANNEL = \"REMOVE_FROM_CHANNEL\";\n    \n    /**\n     * 消息被删除事件\n     */\n    public static final String MESSAGE_REMOVED = \"MESSAGE_REMOVED\";\n    \n    /**\n     * 频道名称改变事件\n     */\n    public static final String CHANNEL_NAME_CHANGED = \"CHANNEL_NAME_CHANGED\";\n    \n    /**\n     * 频道成员数量变更通知\n     */\n    public static final String MEMBERS_COUNT_CHANGED = \"MEMBERS_COUNT_CHANGED\";\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/PublishKeys.java",
    "content": "package org.leo.im.notification;\n\n/**\n * 发布key常量类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class PublishKeys {\n\n    /**\n     * 私聊消息频道\n     */\n    public static final String PRIVATE_MESSAGE_CHANNEL;\n    \n    /**\n     * 群聊消息频道\n     */\n    public static final String GROUP_MESSAGE_CHANNEL;\n    \n    /**\n     * 系统消息频道\n     */\n    public static final String SYSTEM_MESSAGE_CHANNEL;\n    \n    static {\n        PRIVATE_MESSAGE_CHANNEL = System.getProperty(\"channel.private.message\") == null ? \"im-channel\" : \n            System.getProperty(\"channel.private.message\");\n        \n        GROUP_MESSAGE_CHANNEL = System.getProperty(\"channel.group.message\") == null ? \"im-channel\" : \n            System.getProperty(\"channel.group.message\");\n        \n        SYSTEM_MESSAGE_CHANNEL = System.getProperty(\"channel.system.message\") == null ? \"im-channel\" : \n            System.getProperty(\"channel.system.message\");\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/Publisher.java",
    "content": "package org.leo.im.notification;\n\n/**\n * 发布者接口\n * \n * @author Leo\n * @date 2018/3/28\n */\npublic interface Publisher {\n    \n    /**\n     * 发布消息\n     * @param channel\n     * @param message\n     */\n    void publish(String channel, String message);\n    \n    /**\n     * 订阅频道\n     * @param subscriber     \n     * @param channels\n     */\n    void subscribe(Subscriber subscriber, String... channels);\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/PublisherFactory.java",
    "content": "package org.leo.im.notification;\n\nimport org.leo.im.notification.Publisher;\nimport org.leo.im.notification.QueuePublisher;\n\n/**\n * 发布者工厂类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class PublisherFactory {\n\n    /**\n     * 创建发布者的实例\n     * \n     * @return\n     */\n    public static Publisher createPublisher() {\n        switch (System.getProperty(\"notification.type\")) {\n        case \"queue\":\n            return QueuePublisher.getInstance();\n        default:\n            return QueuePublisher.getInstance();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/QueuePublisher.java",
    "content": "package org.leo.im.notification;\n\nimport java.util.Map;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.LinkedBlockingQueue;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 队列发布者实现类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class QueuePublisher implements Publisher {\n\n    private static final int QUEUE_CAPACITY;\n\n    private static final Logger logger = LoggerFactory.getLogger(QueuePublisher.class);\n\n    static {\n        Integer queueCapacity = Integer.getInteger(\"notification.capacity\");\n        QUEUE_CAPACITY = (queueCapacity == null ? 10240 : queueCapacity);\n    }\n\n    /**\n     * 包含频道的哈希表\n     */\n    private Map<String, BlockingQueue<String>> channels = new ConcurrentHashMap<>(2);\n\n    private QueuePublisher() {\n\n    }\n\n    private static class InstanceHolder {\n        private static QueuePublisher instance = new QueuePublisher();\n    }\n\n    public static QueuePublisher getInstance() {\n        return InstanceHolder.instance;\n    }\n\n    /**\n     * 发布消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void publish(String channel, String message) {\n        BlockingQueue<String> queue = channels.get(message);\n        if (queue == null) {\n            queue = new LinkedBlockingQueue<String>(QUEUE_CAPACITY);\n            BlockingQueue<String> returnQueue = this.channels.putIfAbsent(channel, queue);\n            if (returnQueue != null) {\n                queue = returnQueue;\n            }\n        }\n        try {\n            queue.put(message);\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage());\n        }\n    }\n\n    /**\n     * 订阅频道\n     * \n     * @param subscriber\n     * @param subscribeChannels\n     */\n    @Override\n    public void subscribe(Subscriber subscriber, String... subscribeChannels) {\n        for (String channel : subscribeChannels) {\n            BlockingQueue<String> queue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);\n            BlockingQueue<String> returnQueue = this.channels.putIfAbsent(channel, queue);\n            final BlockingQueue<String> subscribeQueue = returnQueue != null ? returnQueue : queue;\n            Thread thread = new Thread(() -> {\n                for (;;) {\n                    final String message = takeFromQueue(subscribeQueue);\n                    if (message != null) {\n                        ThreadPoolHolder.getThreadPool().execute(() -> {\n                            subscriber.onMessage(channel, message);\n                        });\n                    }\n                }\n            });\n            thread.setName(\"SubscriberThread-\" + channel);\n            thread.start();\n        }\n    }\n\n    /**\n     * 从队列中获取数据\n     * \n     * @param subscribeQueue\n     * @return\n     */\n    private String takeFromQueue(BlockingQueue<String> subscribeQueue) {\n        try {\n            return subscribeQueue.take();\n        } catch (InterruptedException e) {\n            logger.error(e.getMessage());\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/Subscriber.java",
    "content": "package org.leo.im.notification;\n\n/**\n * 订阅者接口\n * \n * @author Leo\n * @date 2018/3/28\n */\npublic interface Subscriber {\n    \n    /**\n     * 接收数据\n     * @param channel\n     * @param message\n     */\n    void onMessage(String channel, String message);\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/ThreadPoolHolder.java",
    "content": "package org.leo.im.notification;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * 线程池持有者\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class ThreadPoolHolder {\n\n    private static final int THREAD_POOL_THREADS;\n\n    private static final int THREAD_POOL_QUEUES;\n\n    static {\n        Integer threads = Integer.getInteger(\"thread.pool.threads\");\n        Integer queues = Integer.getInteger(\"thread.pool.queues\");\n        THREAD_POOL_THREADS = threads == null ? Runtime.getRuntime().availableProcessors() * 2 : threads;\n        THREAD_POOL_QUEUES = queues == null ? 512 : queues;\n    }\n\n    // 业务线程池\n    private final static ExecutorService THREAD_POOL = new ThreadPoolExecutor(THREAD_POOL_THREADS, THREAD_POOL_THREADS,\n            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(THREAD_POOL_QUEUES), new ThreadFactory() {\n                private AtomicInteger threadIndex = new AtomicInteger(0);\n\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"handlerThread_\" + this.threadIndex.incrementAndGet());\n                }\n            }, new ThreadPoolExecutor.DiscardPolicy());\n\n    /**\n     * 得到线程池\n     * \n     * @return\n     */\n    public static ExecutorService getThreadPool() {\n        return THREAD_POOL;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/AvatarChangedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 头像改变事件\n * \n * @author Leo\n * @date 2018/5/14\n */\npublic class AvatarChangedEvent implements NotificationEvent {\n    \n    private String userId;\n\n    private String avatar;\n\n    public AvatarChangedEvent(String userId, String avatar) {\n        this.userId = userId;\n        this.avatar = avatar;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.AVATAR_CHANGED);\n        message.put(\"userId\", userId);\n        message.put(\"avatar\", avatar);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelCreatedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 创建频道事件处理器\n * \n * @author Leo\n * @date 2018/5/27\n */\npublic class ChannelCreatedEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private String userIds;\n\n    public ChannelCreatedEvent(String channelId, String userIds) {\n        this.channelId = channelId;\n        this.userIds = userIds;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.CREATE_CHANNEL);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"userIds\", this.userIds);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelNameChangedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 频道名称改变事件\n * \n * @author Leo\n * @date 2018/6/8\n */\npublic class ChannelNameChangedEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private String channelName;\n    \n    public ChannelNameChangedEvent(String channelId, String channelName) {\n        this.channelId = channelId;\n        this.channelName = channelName;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.CHANNEL_NAME_CHANGED);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"channelName\", this.channelName);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/ChannelRemovedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 频道被删除事件\n * \n * @author Leo\n * @date 2018/6/12\n */\npublic class ChannelRemovedEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    public ChannelRemovedEvent(String channelId) {\n        this.channelId = channelId;\n    }\n\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.CHANNEL_REMOVED);\n        message.put(\"channelId\", this.channelId);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/JoinChannelEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 加入频道事件\n * \n * @author Leo\n * @date 2018/5/28\n */\npublic class JoinChannelEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private String[] userIds;\n    \n    private String[] userNicknames;\n    \n    private String admin;\n    \n    private boolean sendBroadcast;\n\n    public JoinChannelEvent(String channelId, String[] userIds, String[] userNicknames, String admin, boolean sendBroadcast) {\n        this.channelId = channelId;\n        this.userIds = userIds;\n        this.userNicknames = userNicknames;\n        this.admin = admin;\n        this.sendBroadcast = sendBroadcast;\n    }\n\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.JOIN_CHANNEL);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"channelType\", \"G\");\n        message.put(\"admin\", this.admin);\n        message.put(\"sendBroadcast\", this.sendBroadcast);\n        \n        JSONArray userIdArray = new JSONArray();\n        if(this.userIds != null) {\n            for(String userId : this.userIds) {\n                JSONObject userIdJSON = new JSONObject();\n                userIdJSON.put(\"id\", userId);\n                userIdArray.add(userIdJSON);\n            }\n        }\n        message.put(\"userIds\", userIdArray);\n        \n        JSONArray userNicknameArray = new JSONArray();\n        if(this.userNicknames != null) {\n            for(String nickname : this.userNicknames) {\n                JSONObject nicknameJSON = new JSONObject();\n                nicknameJSON.put(\"nickname\", nickname);\n                userNicknameArray.add(nicknameJSON);\n            }\n        }\n        message.put(\"userNicknames\", userNicknameArray);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/LeaveChannelEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 离开频道事件\n * \n * @author Leo\n * @date 2018/6/11\n */\npublic class LeaveChannelEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private String userId;\n    \n    private String userNickname;\n    \n    private boolean sendBroadcast;\n    \n    public LeaveChannelEvent(String channelId, String userId, String userNickname, boolean sendBroadcast) {\n        this.channelId = channelId;\n        this.userId = userId;\n        this.userNickname = userNickname;\n        this.sendBroadcast = sendBroadcast;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.LEAVE_CHANNEL);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"channelType\", \"G\");\n        message.put(\"sendBroadcast\", this.sendBroadcast);\n        message.put(\"userId\", this.userId);\n        message.put(\"userNickname\", this.userNickname);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());        \n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/MembersCountChangedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 成员数量变更事件\n * \n * @author Leo\n * @date 2018/6/11\n */\npublic class MembersCountChangedEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private int count;\n    \n    public MembersCountChangedEvent(String channelId, int count) {\n        this.channelId = channelId;\n        this.count = count;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.MEMBERS_COUNT_CHANGED);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"count\", this.count);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/MessageRemovedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 消息被删除事件\n * @author Leo\n * @date 2018/6/5\n */\npublic class MessageRemovedEvent implements NotificationEvent {\n    \n    private long messageId;\n    \n    private String senderId;\n    \n    private String channelId;\n    \n    private String toUserId;\n    \n    public MessageRemovedEvent(long messageId, String senderId, String channelId, String toUserId) {\n        this.messageId = messageId;\n        this.senderId = senderId;\n        this.channelId = channelId;\n        this.toUserId = toUserId;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.MESSAGE_REMOVED);\n        message.put(\"messageId\", this.messageId);\n        message.put(\"senderId\", this.senderId);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"toUserId\", toUserId);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/NewMessageEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 收到新消息事件\n * \n * @author Leo\n * @date 2018/5/16\n */\npublic class NewMessageEvent implements NotificationEvent {\n    \n    private JSONObject message;\n\n    public NewMessageEvent(JSONObject message) {\n        this.message = message;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        message.put(\"action\", ActionNames.NEW_MESSAGE);\n        // 群发、私聊、系统消息\n        if(message.containsKey(\"type\")) {\n            PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n            return;\n        }\n        if(message.containsKey(\"userIds\")) {\n            PublisherFactory.createPublisher().publish(PublishKeys.PRIVATE_MESSAGE_CHANNEL, message.toJSONString());\n            return;\n        }\n        PublisherFactory.createPublisher().publish(PublishKeys.GROUP_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/NicknameChangedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 昵称改变事件\n * \n * @author Leo\n * @date 2018/5/14\n */\npublic class NicknameChangedEvent implements NotificationEvent {\n    \n    private String userId;\n\n    private String nickname;\n\n    public NicknameChangedEvent(String userId, String nickname) {\n        this.userId = userId;\n        this.nickname = nickname;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.NICKNAME_CHANGED);\n        message.put(\"userId\", userId);\n        message.put(\"nickname\", nickname);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/NotificationEvent.java",
    "content": "package org.leo.im.notification.event;\n\n/**\n * 通知事件接口\n * \n * @author Leo\n * @date 2018/5/10\n */\npublic interface NotificationEvent {\n\n    /**\n     * 触发事件\n     */\n    void trigger();\n    \n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/OnlineStatusChangedEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 在线状态改变事件\n * \n * @author Le\n * @date 2018/5/10\n */\npublic class OnlineStatusChangedEvent implements NotificationEvent {\n\n    private String userId;\n\n    private String onlineStatus;\n\n    public OnlineStatusChangedEvent(String userId, String onlineStatus) {\n        this.userId = userId;\n        this.onlineStatus = onlineStatus;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.ONLINE_STATUS_CHANGED);\n        message.put(\"userId\", userId);\n        message.put(\"onlineStatus\", onlineStatus);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/ReadMessageEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 读取消息事件\n * \n * @author Leo\n * @date 2018/5/22\n */\npublic class ReadMessageEvent implements NotificationEvent {\n    \n    private String userId;\n    \n    private String channelId;\n    \n    private short total;\n    \n    private boolean readAll;\n    \n    public ReadMessageEvent(String userId, String channelId, short total, boolean readAll) {\n        this.userId = userId;\n        this.channelId = channelId;\n        this.total = total;\n        this.readAll = readAll;\n    }\n    \n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.READ_MESSAGE);\n        message.put(\"userId\", userId);\n        message.put(\"channelId\", channelId);\n        message.put(\"total\", total);\n        message.put(\"readAll\", readAll);\n        PublisherFactory.createPublisher().publish(PublishKeys.PRIVATE_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/main/java/org/leo/im/notification/event/RemoveFromChannelEvent.java",
    "content": "package org.leo.im.notification.event;\n\nimport org.leo.im.notification.ActionNames;\nimport org.leo.im.notification.PublishKeys;\nimport org.leo.im.notification.PublisherFactory;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 从频道被删除事件\n * \n * @author Leo\n * @date 2018/6/11\n */\npublic class RemoveFromChannelEvent implements NotificationEvent {\n    \n    private String channelId;\n    \n    private String[] userIds;\n    \n    private String[] userNicknames;\n    \n    private String admin;\n    \n    private boolean sendBroadcast;\n    \n    public RemoveFromChannelEvent(String channelId, String[] userIds, String[] userNicknames, String admin, boolean sendBroadcast) {\n        this.channelId = channelId;\n        this.userIds = userIds;\n        this.userNicknames = userNicknames;\n        this.admin = admin;\n        this.sendBroadcast = sendBroadcast;\n    }\n\n    /**\n     * 触发事件\n     */\n    @Override\n    public void trigger() {\n        JSONObject message = new JSONObject();\n        message.put(\"action\", ActionNames.REMOVE_FROM_CHANNEL);\n        message.put(\"channelId\", this.channelId);\n        message.put(\"channelType\", \"G\");\n        message.put(\"admin\", this.admin);\n        message.put(\"sendBroadcast\", this.sendBroadcast);\n        \n        JSONArray userIdArray = new JSONArray();\n        if(this.userIds != null) {\n            for(String userId : this.userIds) {\n                JSONObject userIdJSON = new JSONObject();\n                userIdJSON.put(\"id\", userId);\n                userIdArray.add(userIdJSON);\n            }\n        }\n        message.put(\"userIds\", userIdArray);\n        \n        JSONArray userNicknameArray = new JSONArray();\n        if(this.userNicknames != null) {\n            for(String nickname : this.userNicknames) {\n                JSONObject nicknameJSON = new JSONObject();\n                nicknameJSON.put(\"nickname\", nickname);\n                userNicknameArray.add(nicknameJSON);\n            }\n        }\n        message.put(\"userNicknames\", userNicknameArray);\n        PublisherFactory.createPublisher().publish(PublishKeys.SYSTEM_MESSAGE_CHANNEL, message.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-notification/src/test/java/org/leo/im/notification/AppTest.java",
    "content": "package org.leo.im.notification;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-notification/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: jedis-2.9.0.jar commons-pool2-2.4.2.jar fastjson-1.2.47.ja\n r slf4j-api-1.7.25.jar logback-classic-1.2.3.jar logback-core-1.2.3.j\n ar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-notification/target/classes/META-INF/maven/org.leo.im/leo-im-notification/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:11 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-notification\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-notification\nartifactId=leo-im-notification\n"
  },
  {
    "path": "leo-im-notification/target/classes/META-INF/maven/org.leo.im/leo-im-notification/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-notification</artifactId>\n\t<name>leo-im-notification</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>redis.clients</groupId>\n\t\t\t<artifactId>jedis</artifactId>\n\t\t\t<version>${jedis.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>${fastjson.version}</version>\n        </dependency>\t\t\t\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-notification/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:16 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-notification\n"
  },
  {
    "path": "leo-im-notification/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": "org\\leo\\im\\notification\\QueuePublisher$1.class\n"
  },
  {
    "path": "leo-im-notification/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\ReadMessageEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\QueuePublisher.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\NicknameChangedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\JoinChannelEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\NewMessageEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\PublishKeys.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\LeaveChannelEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\ChannelNameChangedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\PublisherFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\Publisher.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\MessageRemovedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\ChannelCreatedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\MembersCountChangedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\OnlineStatusChangedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\ChannelRemovedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\NotificationEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\ActionNames.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\ThreadPoolHolder.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\AvatarChangedEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\event\\RemoveFromChannelEvent.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\main\\java\\org\\leo\\im\\notification\\Subscriber.java\n"
  },
  {
    "path": "leo-im-notification/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-notification/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-notification\\src\\test\\java\\org\\leo\\im\\notification\\AppTest.java\n"
  },
  {
    "path": "leo-im-notification/target/surefire-reports/TEST-org.leo.im.notification.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.notification.AppTest\" time=\"0.001\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.notification.AppTest\" name=\"testApp\" time=\"0.001\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-notification/target/surefire-reports/org.leo.im.notification.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.notification.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.084 sec\n"
  },
  {
    "path": "leo-im-service/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-service/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-service</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-service/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-service/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-service/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-service/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-service</artifactId>\n\t<name>leo-im-service</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-store</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-notification</artifactId>\n            <version>${project.version}</version>\n        </dependency>\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>${fastjson.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/ChannelServiceImpl.java",
    "content": "package org.leo.im.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.leo.im.api.dto.ChannelDTO;\nimport org.leo.im.api.dto.ChannelListDTO;\nimport org.leo.im.api.dto.ChannelMemberDTO;\nimport org.leo.im.api.service.ChannelService;\nimport org.leo.im.common.data.Page;\nimport org.leo.im.model.Channel;\nimport org.leo.im.model.ChannelMember;\nimport org.leo.im.model.User;\nimport org.leo.im.model.UserChannel;\nimport org.leo.im.notification.event.ChannelCreatedEvent;\nimport org.leo.im.notification.event.ChannelNameChangedEvent;\nimport org.leo.im.notification.event.ChannelRemovedEvent;\nimport org.leo.im.notification.event.JoinChannelEvent;\nimport org.leo.im.notification.event.MembersCountChangedEvent;\nimport org.leo.im.notification.event.NotificationEvent;\nimport org.leo.im.notification.event.RemoveFromChannelEvent;\nimport org.leo.im.store.dao.ChannelDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.factory.DAOFactory;\nimport org.leo.im.util.BeanUtils;\n\n/**\n * 频道服务实现类\n * \n * @author Leo\n * @date 2018/4/3\n */\npublic final class ChannelServiceImpl implements ChannelService {\n\n    /**\n     * 得到频道列表\n     * \n     * @param parameters\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<ChannelListDTO> listChannel(Map<String, Object> parameters, int limit) {\n        List<Channel> list = DAOFactory.createChannelDAO().list(parameters, limit);\n        List<ChannelListDTO> dtoList = new ArrayList<>(list.size());\n        for (Channel channel : list) {\n            ChannelListDTO dto = new ChannelListDTO();\n            BeanUtils.copyProperties(channel, dto);\n            dtoList.add(dto);\n        }\n        return dtoList;\n    }\n\n    /**\n     * 得到群组频道列表\n     * \n     * @param parameters\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<ChannelListDTO> listGroupChannel(Map<String, Object> parameters, int limit) {\n        List<Channel> list = DAOFactory.createChannelDAO().listGroupChannel(parameters, new String[] { \"O\", \"P\" },\n                limit);\n        List<ChannelListDTO> dtoList = new ArrayList<>(list.size());\n        for (Channel channel : list) {\n            ChannelListDTO dto = new ChannelListDTO();\n            BeanUtils.copyProperties(channel, dto);\n            dtoList.add(dto);\n        }\n        return dtoList;\n    }\n\n    /**\n     * 添加频道\n     * \n     * @param dto\n     * @param creatorNickname\n     * @return\n     */\n    @Override\n    public ChannelDTO saveChannel(ChannelDTO dto, String creatorNickname) {\n        ChannelDAO channelDAO = DAOFactory.createChannelDAO();\n        Channel channel = new Channel();\n        BeanUtils.copyProperties(dto, channel);\n        User creator = new User();\n        creator.setId(dto.getCreatorId());\n        channel.setCreator(creator);\n        if (dto.getFromUserId() != null && !dto.getFromUserId().trim().isEmpty()) {\n            User fromUser = new User();\n            fromUser.setId(dto.getFromUserId());\n            fromUser.setName(dto.getFromUsername());\n            fromUser.setNickname(dto.getFromUserNickname());\n            channel.setFrom(fromUser);\n        }\n        if (dto.getToUserId() != null && !dto.getToUserId().trim().isEmpty()) {\n            User toUser = new User();\n            toUser.setId(dto.getToUserId());\n            toUser.setName(dto.getToUsername());\n            toUser.setNickname(dto.getToUserNickname());\n            channel.setTo(toUser);\n        }\n        Channel returnChannel = channelDAO.save(channel);\n        boolean channelExists = false;\n        if (returnChannel == null) {\n            // channel已存在，从数据库中查询已存在的channel。\n            returnChannel = channelDAO.getByFromAndTo(dto.getFromUserId(), dto.getToUserId());\n            channelExists = true;\n        }\n        if (returnChannel == null) {\n            throw new DAOException(\"Channel not exists\");\n        }\n        if(channelExists && dto.getType().equals(\"P\")) {\n            // 删除隐藏频道\n            DAOFactory.createHideChannelDAO().remove(dto.getCreatorId(), returnChannel.getId());\n        }\n        if (!channelExists) {\n            // 得到对方用户（ToUser）的在线状态\n            if(dto.getType().equals(\"P\")) {\n                User toUser = DAOFactory.createUserDAO().getById(dto.getToUserId());\n                if(toUser != null) {\n                    returnChannel.getTo().setOnlineStatus(toUser.getOnlineStatus());\n                }\n            }\n            \n            // 保存频道成员\n            List<ChannelMember> members = this.getMembers(dto);\n            DAOFactory.createChannelMemberDAO().save(returnChannel.getId(), members);\n\n            // 保存用户频道信息\n            List<UserChannel> userChannels = this.getUserChannels(channel, members);\n            DAOFactory.createUserChannelDAO().batchSave(userChannels);\n\n            // 保存未读消息数量\n            if (dto.getType().equals(\"P\")) {\n                // 私聊\n                String[] userIds = new String[] { dto.getFromUserId(), dto.getToUserId() };\n                DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, returnChannel.getId(), (short)0);\n            } else {\n                // 群聊\n                String[] userIds = new String[dto.getMemberCount()];\n                for(int i = 0; i < dto.getMemberCount(); i++) {\n                    userIds[i] = dto.getMembers().get(i).getId();\n                }\n                DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, returnChannel.getId(), (short)0);\n            }\n            this.publishChannelCreatedEvent(returnChannel, members);\n            this.publishJoinChannelEvent(returnChannel, members, creatorNickname, false);\n        }\n        return getDTOFromChannel(returnChannel);\n    }\n\n    /**\n     * 根据id得到频道信息\n     * \n     * @param id\n     * @return\n     */\n    @Override\n    public ChannelDTO getById(String id) {\n        Channel channel = DAOFactory.createChannelDAO().getById(id);\n        if (channel == null) {\n            return null;\n        }\n\n        ChannelDTO dto = new ChannelDTO();\n        BeanUtils.copyProperties(channel, dto);\n        dto.setCreatorId(channel.getCreator().getId());\n        return dto;\n    }\n    \n    /**\n     * 得到用户是否为频道的管理员\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public boolean isAdmin(String userId, String channelId) {\n        return DAOFactory.createChannelMemberDAO().isAdmin(userId, channelId);\n    }\n    \n    /**\n     * 更新频道名称\n     * @param channelId\n     * @param name\n     * @return\n     */\n    @Override\n    public int updateName(String channelId, String name) {\n        int count = DAOFactory.createChannelDAO().updateStringField(channelId, \"name\", name);\n        if(count > 0) {\n            this.publishNameChangedEvent(channelId, name);\n        }\n        return count;\n    }\n    \n    /**\n     * 更新频道用途\n     * @param channelId\n     * @param purpose\n     * @return\n     */\n    @Override\n    public int updatePurpose(String channelId, String purpose) {\n        return DAOFactory.createChannelDAO().updateStringField(channelId, \"purpose\", purpose);\n    }\n    \n    /**\n     * 添加频道成员\n     * @param channelId\n     * @param userIds\n     * @param userNicknames\n     * @param admin\n     * @return\n     */\n    @Override\n    public int addMember(String channelId, String[] userIds, String[] userNicknames, String admin) {\n        List<ChannelMember> members = new ArrayList<>(userIds.length);\n        for(int i = 0; i < userIds.length; i++) {\n            ChannelMember member = new ChannelMember();\n            User user = new User();\n            user.setId(userIds[i]);\n            user.setNickname(userNicknames[i]);\n            member.setUser(user);\n            member.setAdmin(false);\n            members.add(member);\n        }\n        int count = DAOFactory.createChannelMemberDAO().save(channelId, members);\n        \n        // 更新组成员数量\n        if(count > 0) {\n            ChannelDAO dao = DAOFactory.createChannelDAO();\n            dao.increaseMemberCount(channelId, count);\n            \n            Channel channel = dao.getById(channelId);\n            if(channel != null) {\n                List<UserChannel> ucs = new ArrayList<>(userIds.length);\n                for(String userId : userIds) {\n                    UserChannel uc = new UserChannel();\n                    uc.setChannel(channel);\n                    User user = new User();\n                    user.setId(userId);\n                    uc.setUser(user);\n                    uc.setDisplayName(channel.getName());\n                    ucs.add(uc);\n                }\n                DAOFactory.createUserChannelDAO().batchSave(ucs);\n                DAOFactory.createUnreadMessageCountDAO().batchSave(userIds, channelId, (short)0);\n                \n                this.publishJoinChannelEvent(channel, members, admin, true);\n            }\n            new MembersCountChangedEvent(channelId, count).trigger();\n        }\n        return count;\n    }\n    \n    /**\n     * 移除组成员\n     * @param channelId\n     * @param memberId\n     * @param memberNickname\n     * @param admin\n     * @return\n     */\n    @Override\n    public int removeMember(String channelId, String memberId, String memberNickname, String admin) {\n        int count = DAOFactory.createChannelMemberDAO().removeMember(channelId, memberId);\n        if(count > 0) {\n            DAOFactory.createChannelDAO().increaseMemberCount(channelId, -1);\n            DAOFactory.createUserChannelDAO().remove(memberId, channelId);\n            DAOFactory.createUnreadMessageCountDAO().remove(channelId, memberId);\n            new MembersCountChangedEvent(channelId, -1).trigger();\n            \n            NotificationEvent event = new RemoveFromChannelEvent(channelId, new String[] { memberId }, new String[] { memberNickname },\n                    admin, true);\n            event.trigger();\n        }\n        return count;\n    }\n    \n    /**\n     * 得到成员列表\n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    @Override\n    public Page<ChannelMemberDTO> listMember(String channelId, String username, int limit, int offset) {\n        Page<ChannelMember> members = DAOFactory.createChannelMemberDAO().listMember(channelId, username, limit, offset);\n        List<ChannelMemberDTO> dtos = new ArrayList<>(members.getRows().size());\n        for(ChannelMember member : members.getRows()) {\n            ChannelMemberDTO dto = new ChannelMemberDTO();\n            dto.setId(member.getUser().getId());\n            dto.setNickname(member.getUser().getNickname());\n            dto.setAdmin(member.getAdmin());\n            dtos.add(dto);\n        }\n        return new Page<ChannelMemberDTO>(members.getTotal(), dtos);\n    }\n    \n    /**\n     * 变更频道管理员\n     * @param channelId\n     * @param memberId\n     * @param isAdmin\n     * @return\n     */\n    @Override\n    public int changeAdmin(String channelId, String memberId, boolean isAdmin) {\n        return DAOFactory.createChannelMemberDAO().changeAdmin(channelId, memberId, isAdmin);\n    }\n    \n    /**\n     * 离开频道\n     * @param channelId\n     * @param memberId\n     * @param memberNickname\n     * @return\n     */\n    @Override\n    public int leaveChannel(String channelId, String memberId, String memberNickname) {\n        int count = DAOFactory.createChannelMemberDAO().removeMember(channelId, memberId);\n        if(count > 0) {\n            DAOFactory.createChannelDAO().increaseMemberCount(channelId, -1);\n            DAOFactory.createUserChannelDAO().remove(memberId, channelId);\n            DAOFactory.createUnreadMessageCountDAO().remove(channelId, memberId);\n            new MembersCountChangedEvent(channelId, -1).trigger();\n        }\n        return count;        \n    }\n    \n    /**\n     * 删除频道\n     * @param channelId\n     * @param adminId\n     */\n    @Override\n    public int removeChannel(String channelId, String adminId) {\n        // 判断删除群组的管理员是否是群组创建人\n        ChannelDAO dao = DAOFactory.createChannelDAO();\n        Channel channel = dao.getById(channelId);\n        if(channel != null) {\n            if(!channel.getCreator().getId().equals(adminId)) {\n                return 0;\n            }\n            int count = dao.remove(channelId);\n            if(count > 0) {\n                NotificationEvent event = new ChannelRemovedEvent(channelId);\n                event.trigger();\n            }\n            return count;\n        }\n        return 0;\n    }\n    \n    /**\n     * 从Channel得到ChannelDTO\n     * @param channel\n     * @return\n     */\n    private ChannelDTO getDTOFromChannel(Channel channel) {\n        ChannelDTO dto = new ChannelDTO();\n        BeanUtils.copyProperties(channel, dto);\n        dto.setCreatorId(channel.getCreator().getId());\n        dto.setFromUserId(channel.getFrom() == null ? null : channel.getFrom().getId());\n        dto.setFromUsername(channel.getFrom() == null ? null : channel.getFrom().getName());\n        dto.setFromUserNickname(channel.getFrom() == null ? null : channel.getFrom().getNickname());\n        dto.setToUserId(channel.getTo() == null ? null : channel.getTo().getId());\n        dto.setToUsername(channel.getTo() == null ? null : channel.getTo().getName());\n        dto.setToUserNickname(channel.getTo() == null ? null : channel.getTo().getNickname());\n        dto.setType(channel.getType());\n        if(dto.getType().equals(\"P\")) {\n            dto.setToUserOnlineStatus(channel.getTo().getOnlineStatus());\n        }\n        return dto;\n    }\n\n    /**\n     * 得到频道成员\n     * \n     * @param dto\n     * @return\n     */\n    private List<ChannelMember> getMembers(ChannelDTO dto) {\n        List<ChannelMember> members = new ArrayList<>(dto.getMemberCount());\n        for (ChannelMemberDTO memberDto : dto.getMembers()) {\n            ChannelMember member = new ChannelMember();\n            User user = new User();\n            user.setId(memberDto.getId());\n            user.setNickname(memberDto.getNickname());\n            member.setUser(user);\n            member.setAdmin(memberDto.getAdmin());\n            members.add(member);\n        }\n        return members;\n    }\n\n    /**\n     * 得到用户频道\n     * \n     * @param channel\n     * @param members\n     * @return\n     */\n    private List<UserChannel> getUserChannels(Channel channel, List<ChannelMember> members) {\n        List<UserChannel> userChannelList = null;\n        if (channel.getType().equals(\"P\")) {\n            // 私聊\n            userChannelList = new ArrayList<>(2);\n            UserChannel userChannel1 = new UserChannel();\n            userChannel1.setUser(channel.getFrom());\n            userChannel1.setChannel(channel);\n            userChannel1.setDisplayName(\n                    channel.getTo().getNickname() != null && !channel.getTo().getNickname().trim().isEmpty()\n                            ? channel.getTo().getNickname() : channel.getTo().getName());\n            userChannel1.setToUser(channel.getTo());\n            userChannelList.add(userChannel1);\n\n            UserChannel userChannel2 = new UserChannel();\n            userChannel2.setUser(channel.getTo());\n            userChannel2.setChannel(channel);\n            userChannel2.setDisplayName(\n                    channel.getFrom().getNickname() != null && !channel.getFrom().getNickname().trim().isEmpty()\n                            ? channel.getFrom().getNickname() : channel.getFrom().getName());\n            userChannel2.setToUser(channel.getFrom());\n            userChannelList.add(userChannel2);\n            return userChannelList;\n        }\n\n        // 群聊\n        userChannelList = new ArrayList<>(members.size());\n        for (ChannelMember member : members) {\n            UserChannel userChannel = new UserChannel();\n            userChannel.setUser(member.getUser());\n            userChannel.setChannel(channel);\n            userChannel.setDisplayName(channel.getName());\n            userChannelList.add(userChannel);\n        }\n        return userChannelList;\n    }\n    \n    /**\n     * 发布频道创建事件\n     * @param channel\n     * @param members\n     */\n    private void publishChannelCreatedEvent(Channel channel, List<ChannelMember> members) {\n        StringBuilder userIds = null;\n        if(channel.getType().equals(\"P\")) {\n            // 私聊\n            userIds = new StringBuilder(66);\n            userIds.append(channel.getFrom().getId()).append(\",\").append(channel.getTo().getId()).append(\",\");\n        } else {\n            userIds = new StringBuilder(33 * channel.getMemberCount());\n            for(ChannelMember member : members) {\n                userIds.append(member.getUser().getId()).append(\",\");\n            }\n        }\n        userIds.setLength(userIds.length() - 1);\n        NotificationEvent event = new ChannelCreatedEvent(channel.getId(), userIds.toString());\n        event.trigger();\n    }\n    \n    /**\n     * 发布加入频道事件\n     * @param channel\n     * @param members\n     * @param admin\n     * @param sendBroadcast\n     */\n    private void publishJoinChannelEvent(Channel channel, List<ChannelMember> members, String admin, boolean sendBroadcast) {\n        String[] userIds = null;\n        String[] userNicknames = null;\n        if(channel.getType().equals(\"G\")) {\n            userIds = new String[members.size()];\n            userNicknames = new String[members.size()];\n            for(int i = 0; i < members.size(); i++) {\n                userIds[i] = members.get(i).getUser().getId();\n                userNicknames[i] = members.get(i).getUser().getNickname();\n            }\n        } else {\n            userIds = new String[] { channel.getTo().getId() };\n        }\n        NotificationEvent event = new JoinChannelEvent(channel.getId(), userIds, userNicknames, admin, sendBroadcast);\n        event.trigger();\n    }\n    \n    /**\n     * 发布频道名称改变事件\n     * @param channelId\n     * @param channelName\n     */\n    private void publishNameChangedEvent(String channelId, String channelName) {\n        NotificationEvent event = new ChannelNameChangedEvent(channelId, channelName);\n        event.trigger();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/MessageServiceImpl.java",
    "content": "package org.leo.im.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.FileDTO;\nimport org.leo.im.api.dto.MessageDTO;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.model.Channel;\nimport org.leo.im.model.File;\nimport org.leo.im.model.Message;\nimport org.leo.im.model.User;\nimport org.leo.im.notification.event.MessageRemovedEvent;\nimport org.leo.im.notification.event.NewMessageEvent;\nimport org.leo.im.notification.event.NotificationEvent;\nimport org.leo.im.notification.event.ReadMessageEvent;\nimport org.leo.im.store.dao.UnreadMessageCountDAO;\nimport org.leo.im.store.factory.DAOFactory;\nimport org.leo.im.util.BeanUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 消息服务实现类\n * \n * @author Leo\n * @date 2018/5/16\n */\npublic final class MessageServiceImpl implements MessageService {\n    \n    private static final Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);\n    \n    /**\n     * 得到消息列表\n     * @param channelId\n     * @param maxCreateAt\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<MessageDTO> listMessage(String channelId, long maxCreateAt, int limit) {\n        List<Message> list = DAOFactory.createMessageDAO().listMessage(channelId, maxCreateAt, limit);\n        List<MessageDTO> dtoList = new ArrayList<>(list.size());\n        for(Message message : list) {\n            MessageDTO dto = new MessageDTO();\n            BeanUtils.copyProperties(message, dto);\n            dto.setSenderId(message.getSender().getId());\n            dto.setSenderName(message.getSender().getName());\n            dto.setSenderNickname(message.getSender().getNickname());\n            dto.setSenderAvatarUrl(message.getSender().getAvatarUrl());\n            dto.setSenderOnlineStatus(message.getSender().getOnlineStatus());\n            dto.setSenderFirstLetterOfName(message.getSender().getFirstLetterOfName());\n            if(message.getFile() != null) {\n                dto.setFileId(message.getFile().getId());\n                dto.setFileName(message.getFile().getName());\n                dto.setFileExtension(message.getFile().getExtension());\n                dto.setFileSize(message.getFile().getSize());\n                dto.setFileMimeType(message.getFile().getMimeType());\n                dto.setImageWidth(message.getFile().getWidth());\n                dto.setImageHeight(message.getFile().getHeight());\n                dto.setImageThumbWidth(message.getFile().getThumbWidth());\n                dto.setImageThumbHeight(message.getFile().getThumbHeight());\n                dto.setFilePath(message.getFile().getPath());\n            }\n            dtoList.add(dto);\n        }\n        return dtoList;\n    }\n    \n    /**\n     * 根据id得到消息\n     * @param id\n     * @return\n     */\n    @Override\n    public MessageDTO getById(long id) {\n        Message message = DAOFactory.createMessageDAO().getById(id);\n        if(message != null) {\n            MessageDTO dto = new MessageDTO();\n            BeanUtils.copyProperties(message, dto);\n            dto.setChannelId(message.getChannel().getId());\n            dto.setSenderId(message.getSender().getId());\n            dto.setSenderName(message.getSender().getName());\n            dto.setSenderNickname(message.getSender().getNickname());\n            dto.setSenderAvatarUrl(message.getSender().getAvatarUrl());\n            dto.setSenderOnlineStatus(message.getSender().getOnlineStatus());\n            dto.setSenderFirstLetterOfName(message.getSender().getFirstLetterOfName());\n            if(message.getFile() != null) {\n                dto.setFileId(message.getFile().getId());\n                dto.setFileName(message.getFile().getName());\n                dto.setFileExtension(message.getFile().getExtension());\n                dto.setFileSize(message.getFile().getSize());\n                dto.setFileMimeType(message.getFile().getMimeType());\n                dto.setImageWidth(message.getFile().getWidth());\n                dto.setImageHeight(message.getFile().getHeight());\n                dto.setImageThumbWidth(message.getFile().getThumbWidth());\n                dto.setImageThumbHeight(message.getFile().getThumbHeight());\n                dto.setFilePath(message.getFile().getPath());\n            }\n            return dto;\n        }\n        return null;\n    }\n    \n    /**\n     * 添加消息\n     * @param dto\n     * @return\n     */\n    @Override\n    public MessageDTO saveMessage(MessageDTO dto) {\n        Message message = new Message();\n        BeanUtils.copyProperties(dto, message);\n        \n        Channel channel = new Channel();\n        channel.setId(dto.getChannelId());\n        message.setChannel(channel);\n        \n        User sender = new User();\n        sender.setId(dto.getSenderId());\n        message.setSender(sender);\n        if(dto.getFileId() != null && !dto.getFileId().trim().isEmpty()) {\n            File file = new File();\n            file.setId(dto.getFileId().trim());\n            message.setFile(file);\n        }\n        \n        long id = DAOFactory.createMessageDAO().save(message);\n        if(id > 0) {\n            // 更新频道的最后发送消息时间\n            DAOFactory.createChannelDAO().updateLastPostAt(dto.getChannelId(), dto.getCreateAt());\n            \n            MessageDTO resultDTO = this.getById(id);\n            if(resultDTO != null) {\n                // 更新用户的未读消息数量\n                Channel channelModel = DAOFactory.createChannelDAO().getById(dto.getChannelId());\n                if(channelModel == null) {\n                    logger.warn(\"The channel is not found, channel id: \" + dto.getChannelId());\n                } else {\n                    if(channelModel.getType().equals(\"P\")) {\n                        // 私聊\n                        String userId = dto.getSenderId().equals(channelModel.getFrom().getId()) ? channelModel.getTo().getId() : channelModel.getFrom().getId();\n                        DAOFactory.createUnreadMessageCountDAO().increase(userId, dto.getChannelId(), (short)1);\n                        \n                        // 删除对方的隐藏频道\n                        DAOFactory.createHideChannelDAO().remove(userId, dto.getChannelId());\n                    } else {\n                        // 群聊\n                        DAOFactory.createUnreadMessageCountDAO().increaseGroupChannel(dto.getChannelId(), new String[]{ dto.getSenderId() }, (short)1);\n                    }\n                    this.publishNewMessageEvent(resultDTO, channelModel);\n                }\n            }\n            return resultDTO;\n        }\n        return null;\n    }\n    \n    /**\n     * 批量添加消息\n     * @param dtos\n     * @return\n     */\n    @Override\n    public int saveMessage(List<MessageDTO> dtos) {\n        List<Message> messages = new ArrayList<>(dtos.size());\n        for(MessageDTO dto : dtos) {\n            Message message = new Message();\n            BeanUtils.copyProperties(dto, message);\n            \n            Channel channel = new Channel();\n            channel.setId(dto.getChannelId());\n            message.setChannel(channel);\n            \n            User sender = new User();\n            sender.setId(dto.getSenderId());\n            message.setSender(sender);\n            messages.add(message);\n        }\n        int total = DAOFactory.createMessageDAO().save(messages);\n        DAOFactory.createChannelDAO().updateLastPostAt(dtos.get(0).getChannelId(), dtos.get(0).getCreateAt());\n        // 更新用户的未读消息数量\n        if (dtos.get(0).getChannelType().equals(\"G\")) {\n            DAOFactory.createUnreadMessageCountDAO().increaseGroupChannel(dtos.get(0).getChannelId(), null, (short)(total > 99 ? 99 : total));\n        }\n        \n        Channel channelModel = DAOFactory.createChannelDAO().getById(dtos.get(0).getChannelId());\n        for(MessageDTO dto : dtos) {\n            this.publishNewMessageEvent(dto, channelModel);\n        }\n        return total;\n    }\n    \n    /**\n     * 读取消息\n     * @param channelId\n     * @param userId\n     * @param total\n     * @return\n     */\n    @Override\n    public int readMessage(String channelId, String userId, short total) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        int count = dao.decrease(userId, channelId, total);\n        boolean readAll = false;\n        if(count == 0) {\n            count = dao.update(userId, channelId, (short)0);\n            readAll = true;\n        }\n        NotificationEvent event = new ReadMessageEvent(userId, channelId, total, readAll);\n        event.trigger();\n        return count; \n    }\n    \n    /**\n     * 删除消息\n     * @param messageId\n     * @param userId\n     * @return\n     */\n    /**\n     * 删除消息\n     * @param messageId\n     * @param senderId\n     * @param channelId\n     * @param toUserId\n     * @return\n     */\n    @Override\n    public int removeMessage(long messageId, String senderId, String channelId, String toUserId) {\n        int count =  DAOFactory.createMessageDAO().remove(messageId, senderId);\n        NotificationEvent event = new MessageRemovedEvent(messageId, senderId, channelId, toUserId);\n        event.trigger();\n        return count; \n    }\n    \n    /**\n     * 添加文件\n     * @param dto\n     * @return\n     */\n    @Override\n    public String saveFile(FileDTO dto) {\n        File file = new File();\n        BeanUtils.copyProperties(dto, file);\n        return DAOFactory.createFileDAO().save(file);\n    }\n    \n    /**\n     * 发布新消息\n     * @param dto\n     * @param channel\n     */\n    private void publishNewMessageEvent(MessageDTO dto, Channel channel) {\n        // 判断是私聊还是群聊\n        JSONObject message = (JSONObject)JSONObject.toJSON(dto);\n        if(dto.getType() != null && !dto.getType().isEmpty()) {\n            message.put(\"type\", dto.getType());\n        }\n        if(channel.getType().equals(\"P\")) {\n            // 私聊\n            message.put(\"userIds\", dto.getSenderId().equals(channel.getFrom().getId()) ? channel.getTo().getId() : channel.getFrom().getId());\n        } else {\n            message.put(\"groupIds\", channel.getId());\n        }\n        NotificationEvent event = new NewMessageEvent(message);\n        event.trigger();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/UnreadMessageCountServiceImpl.java",
    "content": "package org.leo.im.service;\n\nimport org.leo.im.api.service.UnreadMessageCountService;\nimport org.leo.im.store.dao.UnreadMessageCountDAO;\nimport org.leo.im.store.factory.DAOFactory;\n\n/**\n * 未读消息数量\n * @author Administrator\n *\n */\npublic class UnreadMessageCountServiceImpl implements UnreadMessageCountService {\n    \n    /**\n     * 批量添加未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int batchSaveUnreadMessageCount(String[] userIds, String channelId, short total) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        return dao.batchSave(userIds, channelId, total);\n    }\n    \n    /**\n     * 更新未读消息数量\n     * @param userId\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Override\n    public int updateUnreadMessageCount(String userId, String channelId, short total) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        return dao.update(userId, channelId, total);\n    }\n    \n    /**\n     * 批量更新未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Override\n    public int batchUpdateUnreadMessageCount(String[] userIds, String channelId, short total) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        return dao.batchUpdate(userIds, channelId, total);\n    }\n    \n    /**\n     * 批量增加未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Override\n    public int batchIncreaseUnreadMessageCount(String[] userIds, String channelId, short quantity) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        return dao.batchIncrease(userIds, channelId, quantity);\n    }\n    \n    /**\n     * 增加未读消息数量\n     * @param userId\n     * @param channelId\n     * @param quantity\n     * @return\n     */\n    @Override\n    public int increaseUnreadMessageCount(String userId, String channelId, short quantity) {\n        UnreadMessageCountDAO dao = DAOFactory.createUnreadMessageCountDAO();\n        return dao.increase(userId, channelId, quantity);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/UserChannelServiceImpl.java",
    "content": "package org.leo.im.service;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.UserChannelDTO;\nimport org.leo.im.api.service.UserChannelService;\nimport org.leo.im.model.UserChannel;\nimport org.leo.im.store.factory.DAOFactory;\n\n/**\n * 用户频道服务实现类\n * \n * @author Lining\n * @date 2018/4/20\n */\npublic final class UserChannelServiceImpl implements UserChannelService {\n\n    /**\n     * 得到用户频道列表\n     * @param userId\n     * @param type\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<UserChannelDTO> listUserChannel(String userId, String type, int limit) {\n        List<UserChannel> list = DAOFactory.createUserChannelDAO().listByUserId(userId, type, limit);\n        List<UserChannelDTO> dtoList = new ArrayList<>(list.size());\n        for(UserChannel userChannel : list) {\n            UserChannelDTO dto = new UserChannelDTO();\n            dto.setChannelId(userChannel.getChannel().getId());\n            dto.setChannelName(userChannel.getChannel().getName());\n            dto.setChannelType(userChannel.getChannel().getType());\n            dto.setChannelDisplayName(userChannel.getDisplayName());\n            dto.setCreatorId(userChannel.getChannel().getCreator().getId());\n            if(dto.getChannelType().equals(\"P\")) {\n                dto.setToUserId(userChannel.getToUser().getId());\n                dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus());\n            }\n            dto.setUnreadMessageCount(userChannel.getUnreadMessageCount());\n            dtoList.add(dto);\n        }\n        return dtoList;\n    }\n    \n    /**\n     * 得到用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public UserChannelDTO get(String userId, String channelId) {\n        UserChannel userChannel = DAOFactory.createUserChannelDAO().get(userId, channelId);\n        if(userChannel == null) {\n            return null;\n        }\n        UserChannelDTO dto = new UserChannelDTO();\n        dto.setChannelId(userChannel.getChannel().getId());\n        dto.setChannelName(userChannel.getChannel().getName());\n        dto.setChannelType(userChannel.getChannel().getType());\n        dto.setChannelDisplayName(userChannel.getDisplayName());\n        dto.setMemberCount(userChannel.getChannel().getMemberCount());\n        dto.setCreatorId(userChannel.getChannel().getCreator().getId());\n        if(dto.getChannelType().equals(\"P\")) {\n            dto.setToUserId(userChannel.getToUser().getId());\n            dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus());\n        }\n        dto.setUnreadMessageCount(userChannel.getUnreadMessageCount());\n        return dto;\n    }\n    \n    /**\n     * 更新用户频道\n     * @param channelId\n     * @param userId\n     * @param displayName\n     * @return\n     */\n    @Override\n    public int updateDisplayName(String channelId, String userId, String displayName) {\n        return DAOFactory.createUserChannelDAO().updateDisplayName(channelId, userId, displayName);\n    }\n    \n    /**\n     * 隐藏频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public int hideChannel(String userId, String channelId) {\n        return DAOFactory.createHideChannelDAO().save(userId, channelId);\n    }\n    \n    /**\n     * 根据名称得到用户频道列表\n     * @param userId\n     * @param name\n     * @param type\n     * @return\n     */\n    @Override\n    public List<UserChannelDTO> listByName(String userId, String name, String type) {\n        List<UserChannel> list = DAOFactory.createUserChannelDAO().listByName(userId, name, type);\n        List<UserChannelDTO> dtoList = new ArrayList<>(list.size());\n        for(UserChannel userChannel : list) {\n            UserChannelDTO dto = new UserChannelDTO();\n            dto.setChannelId(userChannel.getChannel().getId());\n            dto.setChannelName(userChannel.getChannel().getName());\n            dto.setChannelType(userChannel.getChannel().getType());\n            dto.setChannelDisplayName(userChannel.getDisplayName());\n            dto.setCreatorId(userChannel.getChannel().getCreator().getId());\n            if(dto.getChannelType().equals(\"P\")) {\n                dto.setToUserId(userChannel.getToUser().getId());\n                dto.setToUserOnlineStatus(userChannel.getToUser().getOnlineStatus());\n            }\n            dto.setUnreadMessageCount(userChannel.getUnreadMessageCount());\n            dtoList.add(dto);\n        }\n        return dtoList;\n    }\n\n}"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/UserServiceImpl.java",
    "content": "package org.leo.im.service;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.leo.im.api.exception.ServiceException;\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.common.data.Page;\nimport org.leo.im.api.dto.UserDTO;\nimport org.leo.im.model.User;\nimport org.leo.im.notification.event.AvatarChangedEvent;\nimport org.leo.im.notification.event.NicknameChangedEvent;\nimport org.leo.im.notification.event.NotificationEvent;\nimport org.leo.im.notification.event.OnlineStatusChangedEvent;\nimport org.leo.im.service.util.PasswordUtils;\nimport org.leo.im.store.dao.UserDAO;\nimport org.leo.im.store.factory.DAOFactory;\nimport org.leo.im.util.BeanUtils;\n\n/**\n * 用户服务实现类\n * \n * @author Leo\n * @date 2018/4/9\n */\npublic final class UserServiceImpl implements UserService {\n    \n    /**\n     * 验证用户登录\n     * @param name\n     * @param password\n     * @return\n     */\n    @Override\n    public UserDTO verifyLogin(String name, String password) {\n        User user = DAOFactory.createUserDAO().getByName(name);\n        if(user == null) {\n            return null;\n        }\n        String md5Password = null;\n        try {\n            md5Password = PasswordUtils.getMd5Password(password, user.getSalt());\n        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {\n            throw new ServiceException(e);\n        }\n        if(md5Password != null && md5Password.equals(user.getPassword())) {\n            UserDTO dto = new UserDTO();\n            BeanUtils.copyProperties(user, dto);\n            return dto;\n        }\n        return null;\n    }\n    \n    /**\n     * 根据id得到用户\n     * @param id\n     * @return\n     */\n    @Override\n    public UserDTO getById(String id) {\n        UserDTO dto = new UserDTO();\n        User user = DAOFactory.createUserDAO().getById(id);\n        if(user != null) {\n            BeanUtils.copyProperties(user, dto);\n        }\n        return dto;\n    }\n    \n    /**\n     * 添加用户\n     * @param dto\n     * @return\n     */\n    @Override\n    public String saveUser(UserDTO dto) {\n        UserDAO dao = DAOFactory.createUserDAO();\n        if(dao.usernameExists(null, dto.getName())) {\n            throw new ServiceException(\"用户 \" + dto.getName() + \" 已存在\");\n        }\n        User user = new User();\n        BeanUtils.copyProperties(dto, user);\n        \n        String salt = PasswordUtils.generateSalt();\n        user.setSalt(salt);\n        \n        String md5Password = null;\n        try {\n            md5Password = PasswordUtils.getMd5Password(user.getPassword(), salt);\n        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {\n            throw new ServiceException(e);\n        }\n        user.setPassword(md5Password);\n        return dao.save(user);\n    }\n    \n    /**\n     * 根据名称或昵称分页查询用户\n     * @param name\n     * @param limit\n     * @param offset\n     * @return\n     */\n    @Override\n    public Page<UserDTO> listByNameOrNickname(String name, int limit, int offset) {\n        Page<User> userResult = DAOFactory.createUserDAO().listByNameOrNickname(name, limit, offset);\n        List<UserDTO> dtoList = new ArrayList<>(userResult.getRows().size());\n        for(User user : userResult.getRows()) {\n            UserDTO dto = new UserDTO();\n            BeanUtils.copyProperties(user, dto);\n            dtoList.add(dto);\n        }\n        return new Page<UserDTO>(userResult.getTotal(), dtoList);\n    }\n    \n    /**\n     * 更新用户\n     * @param dto\n     * @param updateNullValueField\n     * @return\n     */\n    @Override\n    public UserDTO updateUser(UserDTO dto, boolean updateNullValueField) {\n        User user = new User();\n        BeanUtils.copyProperties(dto, user);\n        if(dto.getPassword() != null && !dto.getPassword().trim().isEmpty()) {\n            String salt = PasswordUtils.generateSalt();\n            user.setSalt(salt);\n            \n            String md5Password = null;\n            try {\n                md5Password = PasswordUtils.getMd5Password(user.getPassword(), salt);\n            } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {\n                throw new ServiceException(e);\n            }\n            user.setPassword(md5Password);\n        }\n        UserDAO dao = DAOFactory.createUserDAO();\n        if(dao.update(user, updateNullValueField) == 1) {\n            publishEvent(dto);\n            User returnUser = dao.getById(dto.getId());\n            UserDTO returnDTO = new UserDTO();\n            BeanUtils.copyProperties(returnUser, returnDTO);\n            return returnDTO;\n        }\n        return null;\n    }\n    \n    /**\n     * \n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    public Page<UserDTO> listNonMembers(String channelId, String username, int limit, int offset) {\n        Page<User> pagedUser = DAOFactory.createUserDAO().listNonMembers(channelId, username, limit, offset);\n        if(pagedUser.getTotal() == 0) {\n            return new Page<UserDTO>(0, new ArrayList<UserDTO>(0));\n        }\n        List<UserDTO> list = new ArrayList<>(pagedUser.getRows().size());\n        for(User user : pagedUser.getRows()) {\n            UserDTO dto = new UserDTO();\n            dto.setId(user.getId());\n            dto.setName(user.getName());\n            dto.setNickname(user.getNickname());\n            dto.setFirstLetterOfName(user.getFirstLetterOfName());\n            dto.setAvatarUrl(user.getAvatarUrl());\n            list.add(dto);\n        }\n        return new Page<UserDTO>(pagedUser.getTotal(), list);\n    }\n    \n    /**\n     * 批量下线用户\n     * @param userIds\n     */\n    @Override\n    public int batchOffline(Set<String> userIds) {\n        return DAOFactory.createUserDAO().offline(userIds);\n    }\n    \n    /**\n     * 修改用户口令\n     * @param userId\n     * @param username\n     * @param oldPassword\n     * @param newPassword\n     * @return\n     */\n    @Override\n    public int updatePassword(String userId, String username, String oldPassword, String newPassword) {\n        if(this.verifyLogin(username, oldPassword) != null) {\n            User user = new User();\n            // 更新口令\n            String salt = PasswordUtils.generateSalt();\n            try {\n                String newMd5Password = PasswordUtils.getMd5Password(newPassword, salt);\n                user.setId(userId);\n                user.setSalt(salt);\n                user.setPassword(newMd5Password);\n                return DAOFactory.createUserDAO().update(user, false);\n            } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {\n                return 0;\n            }\n        }\n        return 0;\n    }\n    \n    /**\n     * 发布消息\n     * @param dto\n     */\n    private void publishEvent(UserDTO dto) {\n        if(dto.getOnlineStatus() != null && !dto.getOnlineStatus().trim().isEmpty()) {\n            NotificationEvent event = new OnlineStatusChangedEvent(dto.getId(), dto.getOnlineStatus());\n            event.trigger();\n        }\n        if(dto.getNickname() != null && !dto.getNickname().trim().isEmpty()) {\n            NotificationEvent event = new NicknameChangedEvent(dto.getId(), dto.getNickname());\n            event.trigger();\n        }\n        if(dto.getAvatarUrl() != null && !dto.getAvatarUrl().trim().isEmpty()) {\n            NotificationEvent event = new AvatarChangedEvent(dto.getId(), dto.getAvatarUrl());\n            event.trigger(); \n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/support/CacheableHolder.java",
    "content": "package org.leo.im.service.support;\n\n/**\n * 缓存注解持有者\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic class CacheableHolder {\n\n    private static final ThreadLocal<Boolean> THREADLOCAL = new ThreadLocal<Boolean>();\n    \n    /**\n     * 设置是否启用缓存注解\n     * @param cacheable\n     */\n    public static void setCacheable(boolean cacheable) {\n        THREADLOCAL.set(cacheable);\n    }\n    \n    /**\n     * 得到是否启用缓存注解\n     * @return\n     */\n    public static boolean getCacheable() {\n        Boolean cacheable = THREADLOCAL.get();\n        return cacheable == null ? false : cacheable;\n    }\n    \n    /**\n     * 删除是否启用缓存注解\n     */\n    public static void remove() {\n        THREADLOCAL.remove();\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/support/ServiceProxy.java",
    "content": "package org.leo.im.service.support;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.sql.Connection;\n\nimport org.leo.im.api.annotation.Cacheable;\nimport org.leo.im.api.annotation.Transactional;\nimport org.leo.im.store.connection.impl.PoolConnectionFactory;\n\n/**\n * 服务代理类\n * \n * @author Leo\n * @date 2018/3/23\n */\npublic final class ServiceProxy implements InvocationHandler {\n\n    /**\n     * 代理目标\n     */\n    private Object target;\n\n    private ServiceProxy(Object target) {\n        this.target = target;\n    }\n\n    /**\n     * 通过Class来生成动态代理对象Proxy\n     * \n     * @param target\n     * @param connectionString\n     *            数据库连接字符串\n     * @return\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T newProxyInstance(Object target) {\n        return (T) java.lang.reflect.Proxy.newProxyInstance(target.getClass().getClassLoader(),\n                target.getClass().getInterfaces(), new ServiceProxy(target));\n    }\n\n    @Override\n    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n        // 得到数据库连接\n        Connection conn = PoolConnectionFactory.getInstance().getConnection();\n\n        // 判断方法是否启用了缓存注解\n        CacheableHolder.setCacheable(method.getAnnotation(Cacheable.class) != null);\n\n        // 是否启用了事务注解\n        if (method.getAnnotation(Transactional.class) == null) {\n            // 执行业务逻辑\n            try {\n                return method.invoke(this.target, args);\n            } finally {\n                PoolConnectionFactory.getInstance().closeConnection();\n                CacheableHolder.remove();\n            }\n        }\n\n        conn.setAutoCommit(false);\n        try {\n            Object obj = method.invoke(this.target, args);\n            conn.commit();\n            return obj;\n        } catch (Throwable t) {\n            conn.rollback();\n            throw t;\n        } finally {\n            PoolConnectionFactory.getInstance().closeConnection();\n            CacheableHolder.remove();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/main/java/org/leo/im/service/util/PasswordUtils.java",
    "content": "package org.leo.im.service.util;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Base64;\n\n/**\n * 密码工具类\n * \n * @author Leo\n * @date 2018/3/20\n */\npublic final class PasswordUtils {\n\n    /**\n     * 盐的长度\n     */\n    private static final int SALT_LENGTH = 16;\n\n    /**\n     * 生成盐\n     * \n     * @return\n     */\n    public static String generateSalt() {\n        byte[] salt = new byte[SALT_LENGTH];\n        SecureRandom sr = new SecureRandom();\n        sr.nextBytes(salt);\n        return Base64.getEncoder().encodeToString(salt);\n    }\n\n    /**\n     * 得到md5加密口令\n     * \n     * @param password\n     * @param salt\n     * @return\n     * @throws NoSuchAlgorithmException\n     * @throws UnsupportedEncodingException\n     */\n    public static String getMd5Password(String password, String salt)\n            throws NoSuchAlgorithmException, UnsupportedEncodingException {\n        MessageDigest md5 = MessageDigest.getInstance(\"MD5\");\n        byte[] bs = md5.digest((password + \"|\" + salt + \"|\").getBytes());\n        return byteToHexString(bs);\n    }\n\n    /**\n     * 将数组转换成16进制字符串\n     * @param salt\n     * @return\n     */\n    private static String byteToHexString(byte[] salt) {\n        StringBuffer hexString = new StringBuffer();\n        for (int i = 0; i < salt.length; i++) {\n            String hex = Integer.toHexString(salt[i] & 0xFF);\n            if (hex.length() == 1) {\n                hex = '0' + hex;\n            }\n            hexString.append(hex.toUpperCase());\n        }\n        return hexString.toString();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-service/src/test/java/org/leo/im/service/AppTest.java",
    "content": "package org.leo.im.service;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-service/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-store-1.0.\n jar mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1\n .9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar cglib-3.2.6.jar asm-6\n .0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar jackson-da\n tabind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core-2.8.9.jar\n  leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2.4.2.jar \n fastjson-1.2.47.jar slf4j-api-1.7.25.jar logback-classic-1.2.3.jar lo\n gback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-service/target/classes/META-INF/maven/org.leo.im/leo-im-service/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:16 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-service\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-service\nartifactId=leo-im-service\n"
  },
  {
    "path": "leo-im-service/target/classes/META-INF/maven/org.leo.im/leo-im-service/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-service</artifactId>\n\t<name>leo-im-service</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-api</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-store</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-notification</artifactId>\n            <version>${project.version}</version>\n        </dependency>\t\t\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t<version>${fastjson.version}</version>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-service/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:18 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-service\n"
  },
  {
    "path": "leo-im-service/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-service/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\ChannelServiceImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\UnreadMessageCountServiceImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\UserServiceImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\support\\ServiceProxy.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\support\\CacheableHolder.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\util\\PasswordUtils.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\MessageServiceImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\main\\java\\org\\leo\\im\\service\\UserChannelServiceImpl.java\n"
  },
  {
    "path": "leo-im-service/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-service/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-service\\src\\test\\java\\org\\leo\\im\\service\\AppTest.java\n"
  },
  {
    "path": "leo-im-service/target/surefire-reports/TEST-org.leo.im.service.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.service.AppTest\" time=\"0.001\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.service.AppTest\" name=\"testApp\" time=\"0.001\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-service/target/surefire-reports/org.leo.im.service.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.service.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.088 sec\n"
  },
  {
    "path": "leo-im-socket/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-socket/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-socket</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-socket/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-socket/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-socket/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-socket/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-socket</artifactId>\n\t<name>leo-im-socket</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.netty</groupId>\n\t\t\t<artifactId>netty-all</artifactId>\n\t\t\t<version>${netty.version}</version>\n\t\t</dependency>\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\t\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-api-provider</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-notification</artifactId>\n            <version>${project.version}</version>\n        </dependency>                    \t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/ChannelIdSet.java",
    "content": "package org.leo.im.socket;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * Channel Id 集合类 \n * 使用读写锁保证一个用户多个连接保存和删除的同步性\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class ChannelIdSet {\n\n    private ReadWriteLock rwLock = new ReentrantReadWriteLock();\n\n    private Set<String> channelIds = new HashSet<>();\n\n    /**\n     * 得到列表长度\n     * \n     * @return\n     */\n    public int size() {\n        try {\n            this.rwLock.writeLock().lock();\n            return this.channelIds.size();\n        } finally {\n            this.rwLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * 得到包含Channel Id的集合\n     * \n     * @return\n     */\n    public Set<String> getSet() {\n        try {\n            rwLock.readLock().lock();\n            return this.channelIds;\n        } finally {\n            rwLock.readLock().unlock();\n        }\n    }\n\n    /**\n     * 添加Channel Id\n     * \n     * @param channelId\n     * @return\n     */\n    public boolean add(String channelId) {\n        try {\n            rwLock.writeLock().lock();\n            return this.channelIds.add(channelId);\n        } finally {\n            rwLock.writeLock().unlock();\n        }\n    }\n\n    /**\n     * 删除Channel Id\n     * \n     * @param channelId\n     * @return\n     */\n    public boolean remove(String channelId) {\n        try {\n            rwLock.writeLock().lock();\n            return this.channelIds.remove(channelId);\n        } finally {\n            rwLock.writeLock().unlock();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/ChannelsHolder.java",
    "content": "package org.leo.im.socket;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport io.netty.channel.Channel;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.util.concurrent.ImmediateEventExecutor;\n\n/**\n * 通道持有者类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class ChannelsHolder {\n    \n    private static final Map<String, Channel> CHANNELS = new ConcurrentHashMap<>(1024);\n    \n    /**\n     * ChannelGroup\n     * group会自动监测里面的channel，当channel断开时，会主动踢出该channel，永远保留当前可用的channel列表 。\n     */\n    private static final Map<String, ChannelGroup> CHANNEL_GROUPS = new ConcurrentHashMap<>(100);\n   \n    /**\n     * 用户和Channel对应关系哈希Map\n     */\n    private static final Map<String, ChannelIdSet> USER_CHANNEL_IDS = new ConcurrentHashMap<>(1024);\n    \n    /**\n     * Channel和用户对应关系哈希Map\n     */\n    private static final Map<String, String> CHANNEL_USER_ID = new ConcurrentHashMap<>(1024);\n    \n    /**\n     * Channel和IM频道的的对应关系哈希Map\n     */\n//    private static final Map<String, String> CHANNEL_IM_CHANNELS = new ConcurrentHashMap<>(1024);\n    \n    public static Map<String, ChannelGroup> getChannelGroups() {\n        return CHANNEL_GROUPS;\n    }\n    \n    /**\n     * 添加Channel\n     * @param userId\n     * @param channel\n     */\n    public static void addChannel(String userId, Channel channel) {\n        if(CHANNEL_GROUPS.containsKey(\"all\")) {\n            CHANNEL_GROUPS.get(\"all\").add(channel);\n        }\n        String channelId = channel.id().asShortText();\n        CHANNELS.putIfAbsent(channelId, channel);\n        ChannelIdSet channels = new ChannelIdSet();\n        channels.add(channelId);\n        ChannelIdSet returnSet = USER_CHANNEL_IDS.putIfAbsent(userId, channels);\n        if(returnSet != null) {\n            returnSet.add(channelId);\n        }\n        \n        CHANNEL_USER_ID.putIfAbsent(channelId, userId);\n    }\n    \n    /**\n     * 删除Channel\n     * @param channelId\n     */\n    public static void removeChannel(String channelId) {\n        CHANNELS.remove(channelId);\n        String currentUserId = CHANNEL_USER_ID.get(channelId);\n        if(currentUserId != null) {\n            ChannelIdSet userIds = USER_CHANNEL_IDS.get(currentUserId);\n            if(userIds != null) {\n                if(userIds.remove(channelId)) {\n                    if(userIds.size() == 0) {\n                        USER_CHANNEL_IDS.remove(currentUserId);\n                    }\n                }\n            }\n        }\n        CHANNEL_USER_ID.remove(channelId);  \n    }\n    \n    /**\n     * 根据频道id得到用户id\n     * @param channelId\n     * @return\n     */\n    public static String getUserIdByChannelId(String channelId) {\n        return CHANNEL_USER_ID.get(channelId);\n    }\n    \n    /**\n     * 根据用户id得到channel集合\n     * @param userId\n     * @return\n     */\n    public static ChannelIdSet getChannelsByUserId(String userId) {\n        return USER_CHANNEL_IDS.get(userId);\n    }\n    \n    /**\n     * 根据channel id得到channel\n     * @param channelId\n     * @return\n     */\n    public static Channel getChannelById(String channelId) {\n        return CHANNELS.get(channelId);\n    }\n    \n    /**\n     * 将channel添加到GroupChannel中\n     * @param groupId\n     * @param channel\n     */\n    public static void addChannelToGroup(String groupId, Channel channel) {\n        DefaultChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);\n        ChannelGroup returnChannelGroup = CHANNEL_GROUPS.putIfAbsent(groupId, channelGroup);\n        if(returnChannelGroup == null) {\n            // 不存在该ChannelGroup，第一次添加。\n            channelGroup.add(channel);\n            return;\n        }\n        // ChannelGroup已经存在\n        returnChannelGroup.add(channel);\n    }\n    \n    /**\n     * 添加IM Channel Id\n     * @param channelId\n     * @param IMChannelId\n     */\n//    public static void addIMChannel(String channelId, String IMChannelId) {\n//        CHANNEL_IM_CHANNELS.put(channelId, IMChannelId);\n//    }\n    \n    /**\n     * 移除IM Channel Id\n     * @param channelId\n     */\n//    public static void removeIMChannel(String channelId) {\n//        CHANNEL_IM_CHANNELS.remove(channelId);\n//    }\n    \n    /**\n     * 得到所用用户id\n     * @return\n     */\n    public static Set<String> getUserIds() {\n        return USER_CHANNEL_IDS.keySet();\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/SocketChannel.java",
    "content": "package org.leo.im.socket;\n\nimport io.netty.channel.Channel;\n\n/**\n * Socket 通道类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class SocketChannel {\n\n    private String userId;\n\n    private Channel channel;\n    \n    @Override\n    public String toString() {\n        return \"SocketChannel [userId=\" + userId + \", channel=\" + channel + \"]\";\n    }\n    \n    public SocketChannel(Channel channel) {\n        this.channel = channel;\n    }\n    \n    public String getUserId() {\n        return this.userId;\n    }\n    public void setUserId(String userId) {\n        this.userId = userId;\n    }\n    \n    public Channel getChannel() {\n        return this.channel;\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/WebSocketChannelInitializer.java",
    "content": "package org.leo.im.socket;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.leo.im.socket.handler.TextWebSocketFrameHandler;\n\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.channel.socket.SocketChannel;\nimport io.netty.handler.codec.http.HttpObjectAggregator;\nimport io.netty.handler.codec.http.HttpServerCodec;\nimport io.netty.handler.stream.ChunkedWriteHandler;\nimport io.netty.util.concurrent.DefaultEventExecutorGroup;\nimport io.netty.util.concurrent.EventExecutorGroup;\nimport io.netty.util.concurrent.RejectedExecutionHandlers;\n\n/**\n * WebSocket 通道初始化类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {\n    \n    /**\n     * 业务线程池线程数\n     */\n    private static int eventExecutorGroupThreads = 0;\n    \n    /**\n     * 业务线程池队列长度\n     */\n    private static int eventExecutorGroupQueues = 0;\n    \n    static {\n        eventExecutorGroupThreads = Integer.getInteger(\"websocket.executor.threads\", 0);\n        if(eventExecutorGroupThreads == 0) {\n            eventExecutorGroupThreads = Runtime.getRuntime().availableProcessors();\n        }\n        \n        eventExecutorGroupQueues = Integer.getInteger(\"websocket.executor.queues\", 0);\n        if(eventExecutorGroupQueues == 0) {\n            eventExecutorGroupQueues = 512;\n        }\n    }\n    \n    /**\n     * 业务线程组\n     */\n    private static final EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(\n            eventExecutorGroupThreads, new ThreadFactory() {\n                private AtomicInteger threadIndex = new AtomicInteger(0);\n                @Override\n                public Thread newThread(Runnable r) {\n                    return new Thread(r, \"WebSocketRequestHandlerThread_\" + this.threadIndex.incrementAndGet());\n                }\n            }, eventExecutorGroupQueues, RejectedExecutionHandlers.reject()); \n\n    @Override\n    protected void initChannel(SocketChannel ch) throws Exception {\n        ChannelPipeline pipeline = ch.pipeline();\n\n        // WebSocket协议本身是基于HTTP协议的，所以要使用HTTP解编码器\n        pipeline.addLast(new HttpServerCodec());\n        // 以块的方式来写的处理器\n        pipeline.addLast(new ChunkedWriteHandler());\n        // Netty是基于分段请求的，HttpObjectAggregator的作用是将请求分段再聚合,参数是聚合字节的最大长度\n        pipeline.addLast(new HttpObjectAggregator(8192));\n        // 文本消息处理器\n        pipeline.addLast(eventExecutorGroup, new TextWebSocketFrameHandler());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/WebSocketServer.java",
    "content": "package org.leo.im.socket;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.leo.im.notification.PublisherFactory;\nimport org.leo.im.socket.subscription.SubscriberFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.buffer.PooledByteBufAllocator;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.group.DefaultChannelGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport io.netty.handler.logging.LogLevel;\nimport io.netty.handler.logging.LoggingHandler;\nimport io.netty.util.concurrent.ImmediateEventExecutor;\n\n/**\n * WebSocket Server\n * \n * @author Leo\n * @date 2018/4/3\n */\npublic class WebSocketServer {\n    \n    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);\n    \n    /**\n     * 监听端口号\n     */\n    private int port;\n    \n    /**\n     * Boss线程数\n     */\n    private int bossThreads = 1;\n    \n    /**\n     * Worker线程数\n     */\n    private int workerThreads = 2;\n    \n    public WebSocketServer(int port) {\n        this.port = port;\n    }\n    \n    public int getBossThreads() {\n        return this.bossThreads;\n    }\n    public void setBossThreads(int bossThreads) {\n        this.bossThreads = bossThreads;\n    }\n    \n    public int getWorkerThreads() {\n        return this.workerThreads;\n    }\n    public void setWorkerThreads(int workerThreads) {\n        this.workerThreads = workerThreads;\n    }\n    \n    /**\n     * 启动服务\n     * @throws InterruptedException \n     */\n    public void start() throws InterruptedException {\n        // 参考：https://www.jianshu.com/p/9a97e667cf84    http://www.importnew.com/21561.html   http://wiki.jikexueyuan.com/project/netty-4-user-guide/implement-websocket-chat-function.html\n        /*\n         * ChannelOption.SO_BACKLOG对应的是TCP/IP协议listen函数中的backlog参数，\n         * 函数listen(int socketfd,int backlog)用来初始化服务端可连接队列，服务端处理客户端连接请求是顺序处理的，所以同一时间只能处理一个客户端连接，\n         * 多个客户端来的时候，服务端将不能处理的客户端连接请求放在队列中等待处理，backlog参数指定了队列的大小 \n         */\n        // BossGroup处理nio的Accept事件（TCP连接）\n        NioEventLoopGroup bossGroup = new NioEventLoopGroup(this.bossThreads);\n        // Worker处理nio的Read和Write事件（通道的I/O事件）\n        NioEventLoopGroup workerGroup = new NioEventLoopGroup(this.workerThreads);\n        \n        try {\n            // handler在初始化时就会执行，而childHandler会在客户端成功connect后才执行。\n            ServerBootstrap bootstrap = new ServerBootstrap();\n            bootstrap.group(bossGroup, workerGroup)\n            .channel(NioServerSocketChannel.class)\n            .option(ChannelOption.SO_BACKLOG, 128)\n            .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)\n            .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)\n            .handler(new LoggingHandler(LogLevel.DEBUG))\n            .childHandler(new WebSocketChannelInitializer());\n            \n            ChannelFuture f = bootstrap.bind(port).sync();\n            logger.info(\"The netty websocket server is now ready to accept requests on port {}\", this.port);\n            \n            // 初始化群组\n            // 考虑到群组可能较多，一次性加载占用资源较大，所以在每个用户登录后，将其加载的群组注册到服务中。\n//            initChannelGroup();\n            ChannelsHolder.getChannelGroups().put(\"all\", new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE));\n            \n            // 订阅消息频道，每个队列对应一个线程，如果规模不大，建议一个队列即可。\n            Set<String> channelSet = new HashSet<>(3);\n            channelSet.add(System.getProperty(\"channel.private.message\"));\n            channelSet.add(System.getProperty(\"channel.group.message\"));\n            channelSet.add(System.getProperty(\"channel.system.message\"));\n            Object[] objs = channelSet.toArray();\n            String[] channels = new String[objs.length];\n            for(int i = 0; i < objs.length; i++) {\n                channels[i] = objs[i].toString();\n            }\n            PublisherFactory.createPublisher().subscribe(SubscriberFactory.createSubscriber(), channels);\n            \n            f.channel().closeFuture().sync();\n        } finally {\n            bossGroup.shutdownGracefully();\n            workerGroup.shutdownGracefully();\n        }\n    }\n    \n    /**\n     * 初始化Netty ChannelGroup\n     */\n//    private void initChannelGroup() {\n//        // 得到所有群组类型的channel\n//        Map<String, Object> parameters = new HashMap<>(1);\n//        parameters.put(\"deleteAt\", 0);\n//        ChannelService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createChannelService());\n//        List<ChannelListDTO> dtoList = serviceProxy.listGroupChannel(parameters, 0);\n//        for(ChannelListDTO dto : dtoList) {\n//            ChannelsHolder.getChannelGroups().put(dto.getId(), new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE));\n//        }\n//        ChannelsHolder.getChannelGroups().put(\"all\", new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE));\n//    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/exception/MessageHandleException.java",
    "content": "package org.leo.im.socket.exception;\n\n/**\n * 消息处理异常类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic final class MessageHandleException extends RuntimeException {\n\n    private static final long serialVersionUID = -2512674268771543792L;\n    \n    public MessageHandleException() {\n    }\n\n    public MessageHandleException(String message) {\n        super(message);\n    }\n\n    public MessageHandleException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public MessageHandleException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/handler/TextWebSocketFrameHandler.java",
    "content": "package org.leo.im.socket.handler;\n\nimport static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.leo.im.api.dto.UserDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.notification.event.NotificationEvent;\nimport org.leo.im.notification.event.OnlineStatusChangedEvent;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.im.socket.ChannelsHolder;\nimport org.leo.im.util.JwtUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.fastjson.JSONException;\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.jsonwebtoken.Claims;\nimport io.netty.buffer.Unpooled;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelFutureListener;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.SimpleChannelInboundHandler;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.handler.codec.http.DefaultFullHttpResponse;\nimport io.netty.handler.codec.http.FullHttpRequest;\nimport io.netty.handler.codec.http.FullHttpResponse;\nimport io.netty.handler.codec.http.HttpResponseStatus;\nimport io.netty.handler.codec.http.QueryStringDecoder;\nimport io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.TextWebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.WebSocketFrame;\nimport io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;\nimport io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;\nimport io.netty.util.CharsetUtil;\n\n/**\n * Http 处理器\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic final class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<Object> {\n    /**\n     * ChannelInboundHandlerAdapter的channelRead方法处理完消息后不会自动释放消息，\n     * 若想自动释放收到的消息，可以使用SimpleChannelInboundHandler。\n     */\n\n    private WebSocketServerHandshaker handshaker;\n\n    private static final Logger logger = LoggerFactory.getLogger(TextWebSocketFrameHandler.class);\n\n    @Override\n    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {\n        if (msg instanceof FullHttpRequest) {\n            // 处理HTTP请求\n            handleHttpRequest(ctx, (FullHttpRequest) msg);\n            return;\n        } \n        if (msg instanceof WebSocketFrame) {\n            // 处理WebSocket请求\n            handleWebSocketFrame(ctx, (WebSocketFrame) msg);\n        }\n\n        // 手动释放消息（SimpleChannelInboundHandler会自动释放）\n        // ReferenceCountUtil.release(msg);\n    }\n\n    @Override\n    public void channelReadComplete(ChannelHandlerContext ctx) {\n        ctx.flush();\n    }\n\n    @Override\n    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {\n        cause.printStackTrace();\n        ctx.close();\n    }\n\n    @Override\n    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {\n        super.handlerRemoved(ctx);\n    }\n    \n    @Override\n    public void channelInactive(ChannelHandlerContext ctx) throws Exception {\n        this.clientOffline(ctx);\n        super.channelInactive(ctx);\n    }\n\n    /**\n     * 处理HTTP请求\n     * \n     * @param ctx\n     * @param request\n     */\n    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {\n        // WebSocket访问，处理握手升级。\n        if (request.headers().get(\"Connection\").equals(\"Upgrade\")) {\n            // Handshake\n            WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(\"\", null, true);\n            handshaker = wsFactory.newHandshaker(request);\n            if (handshaker == null) {\n                // 无法处理的WebSocket版本\n                WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());\n                return;\n            }\n\n            // 验证token\n            String token = getRequestParameter(request, \"token\");\n            if (token == null) {\n                FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.UNAUTHORIZED,\n                        Unpooled.copiedBuffer(\"Token not found in request url\", CharsetUtil.UTF_8));\n                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);\n                logger.error(\"Token not found in request url\");\n                return;\n            }\n            JSONObject json = null;\n            try {\n                Claims claims = JwtUtils.parseJWT(token, System.getProperty(\"jwt.secret\"));\n                String subject = claims.getSubject();\n                json = JSONObject.parseObject(subject);\n            } catch (Exception e) {\n                FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.UNAUTHORIZED,\n                        Unpooled.copiedBuffer(\"Token is not available\", CharsetUtil.UTF_8));\n                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);\n                logger.error(\"Token is not available\");\n                return;\n            }\n\n            // 向客户端发送WebSocket握手，完成握手。\n            final String userId = json.getString(\"userId\");\n            ChannelFuture channelFuture = handshaker.handshake(ctx.channel(), request);\n            channelFuture.addListener(new ChannelFutureListener() {\n                @Override\n                public void operationComplete(ChannelFuture future) throws Exception {\n                    if (!future.isSuccess()) {\n                        future.channel().close();\n                        return;\n                    }\n                    // 加入到ChannelHolders中\n                    ChannelsHolder.addChannel(userId, future.channel());\n                }\n            });\n            return;\n        }\n        // 普通的HTTP访问\n        logger.warn(\"无效的访问: {}\", request.uri());\n    }\n\n    /**\n     * 处理WebSocket请求\n     * \n     * @param ctx\n     * @param frame\n     */\n    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {\n        if (frame instanceof CloseWebSocketFrame) {\n            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());\n            ctx.close();\n            return;\n        }\n        // 没有使用WebSocketServerProtocolHandler，所以不会接收到PingWebSocketFrame。\n        // if (frame instanceof PingWebSocketFrame) {\n        // ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));\n        // return;\n        // }\n        if (!(frame instanceof TextWebSocketFrame)) {\n            throw new UnsupportedOperationException(\n                    String.format(\"%s frame types not supported\", frame.getClass().getName()));\n        }\n\n        String request = ((TextWebSocketFrame) frame).text();\n        logger.debug(\"收到客户端发送的数据：\" + request);\n        // 回复心跳\n        if (request.length() == 0) {\n            ctx.writeAndFlush(new TextWebSocketFrame(\"\"));\n            return;\n        }\n        this.handleMessage(ctx.channel(), request);\n    }\n\n    /**\n     * 得到Http请求的Query String\n     * \n     * @param req\n     * @param name\n     * @return\n     */\n    private static String getRequestParameter(FullHttpRequest req, String name) {\n        QueryStringDecoder decoder = new QueryStringDecoder(req.uri());\n        Map<String, List<String>> parameters = decoder.parameters();\n        Set<Entry<String, List<String>>> entrySet = parameters.entrySet();\n        for (Entry<String, List<String>> entry : entrySet) {\n            if (entry.getKey().equalsIgnoreCase(name)) {\n                return entry.getValue().get(0);\n            }\n        }\n        return null;\n    }\n    \n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    private void handleMessage(Channel channel, String message) {\n        JSONObject json = null;\n        try {\n            json = JSONObject.parseObject(message);\n        } catch(JSONException e) {\n            logger.error(e.getMessage());\n            return;\n        }\n        if(!json.containsKey(\"action\")) {\n            logger.warn(\"can not find action.\");\n            return;\n        }\n        switch(json.getString(\"action\").toUpperCase()) {\n        case \"BIND_GROUP_CHANNEL\":\n            bindChannelToGroupChannel(json, channel);\n            break;\n        case \"REMOVE_CHANNEL_FROM_GROUP\":\n            removeChannelFromGroupChannel(json, channel);\n            break;\n        }\n    }\n    \n    /**\n     * 绑定channel到ChannelGroup中\n     * @param message\n     * @param channel\n     */\n    private void bindChannelToGroupChannel(JSONObject message, Channel channel) {\n        String[] groupIds = message.getString(\"groupIds\").split(\",\");\n        for(int i = 0; i < groupIds.length; i++) {\n            if(groupIds[i] != null && !groupIds[i].trim().isEmpty()) {\n                ChannelsHolder.addChannelToGroup(groupIds[i], channel);\n            }\n        }\n    }\n    \n    /**\n     * 从GroupChannel中删除channel\n     * @param message\n     * @param channel\n     */\n    private void removeChannelFromGroupChannel(JSONObject message, Channel channel) {\n        String imChannelId = message.getString(\"channelId\");\n        ChannelGroup cg = ChannelsHolder.getChannelGroups().get(imChannelId);\n        if(cg != null) {\n            cg.remove(channel);\n        }\n    }\n    \n    /**\n     * 客户端下线\n     * @param ctx\n     */\n    private void clientOffline(ChannelHandlerContext ctx) {\n        String channelId = ctx.channel().id().asShortText();\n        ctx.close();\n        String userId = ChannelsHolder.getUserIdByChannelId(channelId);\n        ChannelsHolder.removeChannel(channelId);\n        \n        // 更新用户在线状态\n        if(userId != null) {\n            UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n            UserDTO dto = new UserDTO();\n            dto.setId(userId);\n            dto.setOnlineStatus(\"offline\");\n            if(serviceProxy.updateUser(dto, false) != null) {\n                NotificationEvent event = new OnlineStatusChangedEvent(dto.getId(), dto.getOnlineStatus());\n                event.trigger();\n            }\n        }        \n    }\n\n}"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/QueueSubscriber.java",
    "content": "package org.leo.im.socket.subscription;\n\nimport org.leo.im.notification.Subscriber;\nimport org.leo.im.socket.exception.MessageHandleException;\nimport org.leo.im.socket.subscription.handler.MessageHandler;\nimport org.leo.im.socket.subscription.handler.MessageHandlerFactory;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 队列订阅器实现类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class QueueSubscriber implements Subscriber {\n\n    /**\n     * 接收数据\n     * @param channel\n     * @param message\n     */\n    @Override\n    public void onMessage(String channel, String message) {\n        if(message == null || message.trim().isEmpty()) {\n            throw new MessageHandleException(\"Message is empty\");\n        }\n        JSONObject json = null;\n        try {\n            json = JSONObject.parseObject(message);\n        } catch(Exception e) {\n            throw new MessageHandleException(\"Message is not a valid json format, \" + message);\n        }\n        MessageHandler handler = MessageHandlerFactory.createMessageHandler(json.getString(\"action\"));\n        if(handler == null) {\n            throw new MessageHandleException(\"Invalid action, \" + json.getString(\"action\"));\n        }\n        handler.doHandle(channel, json.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/SubscriberFactory.java",
    "content": "package org.leo.im.socket.subscription;\n\nimport org.leo.im.notification.Subscriber;\n\n/**\n * 订阅器工厂类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class SubscriberFactory {\n\n    /**\n     * 创建订阅器\n     * \n     * @return\n     */\n    public static Subscriber createSubscriber() {\n        switch (System.getProperty(\"notification.type\")) {\n        case \"queue\":\n            return new QueueSubscriber();\n        default:\n            return new QueueSubscriber();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/AbstractMessageHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport org.leo.im.socket.ChannelIdSet;\nimport org.leo.im.socket.ChannelsHolder;\n\nimport io.netty.channel.Channel;\nimport io.netty.channel.group.ChannelGroup;\nimport io.netty.handler.codec.http.websocketx.TextWebSocketFrame;\n\n/**\n * 消息处理抽象类\n * \n * @author Leo\n * @date 2018/5/15\n */\npublic class AbstractMessageHandler {\n    \n    private String[] groups = null;\n    \n    private String[] users = null;\n    \n    protected void setGroups(String... groups) {\n        this.groups = groups;\n    }\n    \n    protected void setUsers(String... users) {\n        this.users = users;\n    }\n    protected String[] getUsers() {\n        return this.users;\n    }\n    \n    /**\n     * 处理消息\n     * @param message\n     */\n    public void handleMessage(String message) {\n        if (this.users != null) {\n            for (String user : users) {\n                if (user != null && !user.trim().isEmpty()) {\n                    ChannelIdSet chIdSet = ChannelsHolder.getChannelsByUserId(user);\n                    if (chIdSet == null) {\n                        continue;\n                    }\n                    for (String channelId : chIdSet.getSet()) {\n                        Channel ch = ChannelsHolder.getChannelById(channelId);\n                        if (ch != null) {\n                            ch.writeAndFlush(new TextWebSocketFrame(message));\n                        }\n                    }\n                }\n            }\n        }\n        if (this.groups != null) {\n            for (String group : groups) {\n                if (group != null && !group.trim().isEmpty()) {\n                    ChannelGroup cg = ChannelsHolder.getChannelGroups().get(group);\n                    if(cg != null) {\n                        cg.writeAndFlush(new TextWebSocketFrame(message));\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/AvatarChangedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\n/**\n * 头像改变消息处理器\n * @author Administrator\n *\n */\npublic class AvatarChangedHandler extends AbstractMessageHandler implements MessageHandler {\n    \n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        super.setGroups(\"all\");\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelCreatedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport java.util.Set;\n\nimport org.leo.im.socket.ChannelIdSet;\nimport org.leo.im.socket.ChannelsHolder;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\nimport io.netty.channel.Channel;\n\n/**\n * 频道创建消息处理器\n * \n * @author Leo\n * @date 2018/5/28\n */\npublic class ChannelCreatedHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSON.parseObject(message);\n        if (!json.containsKey(\"userIds\")) {\n            return;\n        }\n        \n        String channelId = json.getString(\"channelId\");\n        String[] userIds = json.getString(\"userIds\").split(\",\");\n        for (String userId : userIds) {\n            ChannelIdSet channelIdSet = ChannelsHolder.getChannelsByUserId(userId);\n            if (channelIdSet != null) {\n                Set<String> imChannelIds = channelIdSet.getSet();\n                for (String imChannelId : imChannelIds) {\n                    Channel imChannel = ChannelsHolder.getChannelById(imChannelId);\n                    if(imChannel == null) {\n                        continue;\n                    }\n                    ChannelsHolder.addChannelToGroup(channelId, imChannel);       \n                }\n            }\n        }\n        super.setUsers(json.getString(\"userIds\").split(\",\"));\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelNameChangedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 频道名称改变处理器\n * \n * @author Leo\n * @date 2018/6/8\n */\npublic class ChannelNameChangedHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSON.parseObject(message);\n        super.setGroups(json.getString(\"channelId\"));\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ChannelRemovedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 频道被删除处理器\n * \n * @author Leo\n * @date 2018/6/12\n */\npublic class ChannelRemovedHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSONObject.parseObject(message);\n        super.setGroups(json.getString(\"channelId\"));\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/JoinChannelHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.MessageDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.service.support.ServiceProxy;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 加入频道消息处理器\n * \n * @author Leo\n * @date 2018/5/28\n */\npublic class JoinChannelHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSON.parseObject(message);\n        JSONArray userIdArray = json.getJSONArray(\"userIds\");\n        String[] userIds = new String[userIdArray.size()];\n        for (int i = 0; i < userIds.length; i++) {\n            userIds[i] = userIdArray.getJSONObject(i).getString(\"id\");\n        }\n        super.setUsers(userIds);\n\n        JSONObject sendMessage = new JSONObject();\n        sendMessage.put(\"action\", json.getString(\"action\"));\n        sendMessage.put(\"channelId\", json.getString(\"channelId\"));\n        super.handleMessage(sendMessage.toJSONString());\n\n        if (json.getString(\"channelType\").equals(\"G\")) {\n            // 发送系统消息\n            JSONArray userNicknames = json.getJSONArray(\"userNicknames\");\n            List<MessageDTO> dtos = new ArrayList<>(userNicknames.size());\n            long createAt = new java.util.Date().getTime();\n            for (int i = 0; i < userNicknames.size(); i++) {\n                MessageDTO dto = new MessageDTO();\n                dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000));\n                dto.setChannelType(\"G\");\n                dto.setType(\"system_add_to_channel\");\n                dto.setContent(userNicknames.getJSONObject(i).getString(\"nickname\") + \" 被 \" + json.getString(\"admin\") + \" 加入到该频道\");\n                dto.setChannelId(json.getString(\"channelId\"));\n                dto.setSenderFirstLetterOfName(\"L\");\n                dto.setSenderNickname(\"系统用户\");\n                dto.setSenderId(\"00000000000000000000000000000000\");\n                dto.setCreateAt(createAt);\n                dtos.add(dto);\n            }\n\n            MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n            serviceProxy.saveMessage(dtos);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/LeaveChannelHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport org.leo.im.api.dto.MessageDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.service.support.ServiceProxy;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 离开频道处理器\n * \n * @author Leo\n * @date 2018/6/12\n */\npublic class LeaveChannelHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject data = JSON.parseObject(message);\n        super.setGroups(data.getString(\"channelId\"));\n\n        JSONObject sendMessage = new JSONObject();\n        sendMessage.put(\"action\", data.getString(\"action\"));\n        sendMessage.put(\"channelId\", data.getString(\"channelId\"));\n        sendMessage.put(\"userId\", data.getString(\"userId\"));\n        sendMessage.put(\"userNickname\", data.getString(\"userNickname\"));\n        super.handleMessage(sendMessage.toJSONString());\n\n        if (data.getString(\"channelType\").equals(\"G\")) {\n            // 发送系统消息\n            long createAt = new java.util.Date().getTime();\n            MessageDTO dto = new MessageDTO();\n            dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000));\n            dto.setChannelType(\"G\");\n            dto.setType(\"system_add_to_channel\");\n            dto.setContent(data.getString(\"userNickname\") + \" 离开该频道\");\n            dto.setChannelId(data.getString(\"channelId\"));\n            dto.setSenderFirstLetterOfName(\"L\");\n            dto.setSenderNickname(\"系统用户\");\n            dto.setSenderId(\"00000000000000000000000000000000\");\n            dto.setCreateAt(createAt);\n\n            MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n            serviceProxy.saveMessage(dto);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MembersCountChangedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 成员数量变更处理器\n * \n * @author Leo\n * @date 2018/6/11\n */\npublic class MembersCountChangedHandler extends AbstractMessageHandler implements MessageHandler {\n    \n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSONObject.parseObject(message);\n        super.setGroups(json.getString(\"channelId\"));\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MessageHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\n/**\n * 消息处理器接口\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic interface MessageHandler {\n    \n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    void doHandle(String channel, String message);\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/MessageHandlerFactory.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport org.leo.im.notification.ActionNames;\n\n/**\n * 消息处理器工厂类\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class MessageHandlerFactory {\n\n    /**\n     * 创建消息处理器\n     * \n     * @param messageType\n     * @return\n     */\n    public static MessageHandler createMessageHandler(String messageType) {\n        switch (messageType) {\n        case ActionNames.ONLINE_STATUS_CHANGED:\n            return new OnlineStatusChangedHandler();\n        case ActionNames.NICKNAME_CHANGED:\n            return new NicknameChangedHandler();\n        case ActionNames.AVATAR_CHANGED:\n            return new AvatarChangedHandler();\n        case ActionNames.NEW_MESSAGE:\n            return new NewMessageHandler();\n        case ActionNames.READ_MESSAGE:\n            return new ReadMessageHandler();\n        case ActionNames.CREATE_CHANNEL:\n            return new ChannelCreatedHandler();\n        case ActionNames.JOIN_CHANNEL:\n            return new JoinChannelHandler();\n        case ActionNames.MESSAGE_REMOVED:\n            return new RemoveMessageHandler();\n        case ActionNames.CHANNEL_NAME_CHANGED:\n            return new ChannelNameChangedHandler();\n        case ActionNames.MEMBERS_COUNT_CHANGED:\n            return new MembersCountChangedHandler();\n        case ActionNames.REMOVE_FROM_CHANNEL:\n            return new RemoveFromChannelHandler();\n        case ActionNames.LEAVE_CHANNEL:\n            return new LeaveChannelHandler();\n        case ActionNames.CHANNEL_REMOVED:\n            return new ChannelRemovedHandler();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/NewMessageHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 新消息处理器\n * \n * @author Leo\n * @date 2018/5/16\n */\npublic class NewMessageHandler extends AbstractMessageHandler implements MessageHandler {\n    \n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSONObject.parseObject(message);\n        if(json.containsKey(\"groupIds\")) {\n            String[] groupIds = json.getString(\"groupIds\").split(\",\");\n            super.setGroups(groupIds);\n        }\n        if(json.containsKey(\"userIds\")) {\n            String[] userIds = json.getString(\"userIds\").split(\",\");\n            super.setUsers(userIds);\n        }\n        json.remove(\"groupIds\");\n        json.remove(\"userIds\");\n        super.handleMessage(json.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/NicknameChangedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\n/**\n * 昵称改变消息处理器\n * \n * @author Leo\n * @date 2018/5/15\n */\npublic class NicknameChangedHandler extends AbstractMessageHandler implements MessageHandler {\n    \n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        super.setGroups(\"all\");\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/OnlineStatusChangedHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\n/**\n * 用户在线状态改变动作处理器\n * \n * @author Leo\n * @date 2018/3/29\n */\npublic class OnlineStatusChangedHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        super.setGroups(\"all\");\n        super.handleMessage(message);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/ReadMessageHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 读取消息处理器\n * \n * @author Leo\n * @date 2018/5/22\n */\npublic class ReadMessageHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * \n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSONObject.parseObject(message);\n        super.setUsers(json.getString(\"userId\"));\n        super.handleMessage(message);\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/RemoveFromChannelHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.api.dto.MessageDTO;\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.MessageService;\nimport org.leo.im.service.support.ServiceProxy;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 从频道被移除处理器\n * \n * @author Leo\n * @date 2018/6/12\n */\npublic class RemoveFromChannelHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     * @param channel\n     * @param message\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSON.parseObject(message);\n        JSONArray userIdArray = json.getJSONArray(\"userIds\");\n        String[] userIds = new String[userIdArray.size()];\n        for (int i = 0; i < userIds.length; i++) {\n            userIds[i] = userIdArray.getJSONObject(i).getString(\"id\");\n        }\n        super.setUsers(userIds);\n\n        JSONObject sendMessage = new JSONObject();\n        sendMessage.put(\"action\", json.getString(\"action\"));\n        sendMessage.put(\"channelId\", json.getString(\"channelId\"));\n        super.handleMessage(sendMessage.toJSONString());\n\n        if (json.getString(\"channelType\").equals(\"G\")) {\n            // 发送系统消息\n            JSONArray userNicknames = json.getJSONArray(\"userNicknames\");\n            List<MessageDTO> dtos = new ArrayList<>(userNicknames.size());\n            long createAt = new java.util.Date().getTime();\n            for (int i = 0; i < userNicknames.size(); i++) {\n                MessageDTO dto = new MessageDTO();\n                dto.setId(System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000));\n                dto.setChannelType(\"G\");\n                dto.setType(\"system_remove_from_channel\");\n                dto.setContent(userNicknames.getJSONObject(i).getString(\"nickname\") + \" 被 \" + json.getString(\"admin\") + \" 移出该频道\");\n                dto.setChannelId(json.getString(\"channelId\"));\n                dto.setSenderFirstLetterOfName(\"L\");\n                dto.setSenderNickname(\"系统用户\");\n                dto.setSenderId(\"00000000000000000000000000000000\");\n                dto.setCreateAt(createAt);\n                dtos.add(dto);\n            }\n\n            MessageService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createMessageService());\n            serviceProxy.saveMessage(dtos);\n        }        \n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/main/java/org/leo/im/socket/subscription/handler/RemoveMessageHandler.java",
    "content": "package org.leo.im.socket.subscription.handler;\n\nimport org.leo.im.notification.ActionNames;\n\nimport com.alibaba.fastjson.JSONObject;\n\n/**\n * 删除消息处理器\n * \n * @author Leo\n * @date 2018/6/5\n */\npublic class RemoveMessageHandler extends AbstractMessageHandler implements MessageHandler {\n\n    /**\n     * 处理消息\n     */\n    @Override\n    public void doHandle(String channel, String message) {\n        JSONObject json = JSONObject.parseObject(message);\n        if(json.containsKey(\"toUserId\") && json.getString(\"toUserId\") != null) {\n            super.setUsers(json.getString(\"toUserId\"));\n        } else {\n            super.setGroups(json.getString(\"channelId\"));\n        }\n        JSONObject sendMessage = new JSONObject();\n        sendMessage.put(\"action\", ActionNames.MESSAGE_REMOVED);\n        sendMessage.put(\"messageId\", json.getLongValue(\"messageId\"));\n        sendMessage.put(\"senderId\", json.getString(\"senderId\"));        \n        super.handleMessage(sendMessage.toJSONString());\n    }\n\n}\n"
  },
  {
    "path": "leo-im-socket/src/test/java/org/leo/im/socket/AppTest.java",
    "content": "package org.leo.im.socket;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-socket/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: netty-all-4.1.24.Final.jar leo-im-util-1.0.jar cglib-3.2.6\n .jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar \n jackson-databind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core\n -2.8.9.jar leo-im-api-1.0.jar leo-im-common-1.0.jar leo-im-api-provid\n er-1.0.jar leo-im-service-1.0.jar leo-im-store-1.0.jar mysql-connecto\n r-java-8.0.11.jar protobuf-java-2.6.0.jar druid-1.1.9.jar leo-im-mode\n l-1.0.jar leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2\n .4.2.jar fastjson-1.2.47.jar slf4j-api-1.7.25.jar logback-classic-1.2\n .3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-socket/target/classes/META-INF/maven/org.leo.im/leo-im-socket/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:19 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-socket\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-socket\nartifactId=leo-im-socket\n"
  },
  {
    "path": "leo-im-socket/target/classes/META-INF/maven/org.leo.im/leo-im-socket/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-socket</artifactId>\n\t<name>leo-im-socket</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>io.netty</groupId>\n\t\t\t<artifactId>netty-all</artifactId>\n\t\t\t<version>${netty.version}</version>\n\t\t</dependency>\t\t\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-util</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-api</artifactId>\n            <version>${project.version}</version>\n        </dependency>\t\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-api-provider</artifactId>\n            <version>${project.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-notification</artifactId>\n            <version>${project.version}</version>\n        </dependency>                    \t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-socket/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:23 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-socket\n"
  },
  {
    "path": "leo-im-socket/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-socket/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\exception\\MessageHandleException.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\AbstractMessageHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\ChannelNameChangedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\NicknameChangedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\RemoveFromChannelHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\SocketChannel.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\MessageHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\ReadMessageHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\ChannelRemovedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\ChannelIdSet.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\WebSocketServer.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\AvatarChangedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\ChannelCreatedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\SubscriberFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\WebSocketChannelInitializer.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\RemoveMessageHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\QueueSubscriber.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\LeaveChannelHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\JoinChannelHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\OnlineStatusChangedHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\ChannelsHolder.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\MessageHandlerFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\handler\\TextWebSocketFrameHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\NewMessageHandler.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\main\\java\\org\\leo\\im\\socket\\subscription\\handler\\MembersCountChangedHandler.java\n"
  },
  {
    "path": "leo-im-socket/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-socket/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-socket\\src\\test\\java\\org\\leo\\im\\socket\\AppTest.java\n"
  },
  {
    "path": "leo-im-socket/target/surefire-reports/TEST-org.leo.im.socket.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.socket.AppTest\" time=\"0.002\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.socket.AppTest\" name=\"testApp\" time=\"0.002\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-socket/target/surefire-reports/org.leo.im.socket.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.socket.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 sec\n"
  },
  {
    "path": "leo-im-starter/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-starter/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-starter</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-starter/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-starter/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-starter/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-starter/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-starter</artifactId>\n\t<name>leo-im-starter</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-http</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-socket</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-migration</artifactId>\n            <version>${parent.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-starter/src/main/conf/app.conf",
    "content": "#服务器类别\n#可选项有http，websocket\nserver.type=http,websocket\n\n#Web前端地址\nhttp.origin=http://localhost:3006\n#Http服务器监听端口\nhttp.port=2006\n#Http服务器boss线程数与worker线程数\n#建议boss为1，worker为2，可根据服务器配置自行调整\nhttp.boss.threads=1\nhttp.worker.threads=2\n\n#Http业务线程池与业务队列设置\n#Http Executor线程处理的Http请求较多，建议将线程数设置为2以上。\n#threads如果设置为0，则使用默认值：CPU核心数*2，如果queues设置为0，则使用默认值1024\nhttp.executor.threads=2\nhttp.executor.queues=256\n\n#Http最大内容长度（默认 为10M）\nhttp.max.content.length=50\n\n#WebSocket监听端口\nwebsocket.port=2007\n#WebSocket服务器boss线程数与worker线程数\n#建议boss为1，worker为1，可根据服务器配置自行调整\nwebsocket.boss.threads=1\nwebsocket.worker.threads=1\n\n#WebSocket业务线程池与业务队列设置\n#WebSocket的Executor线程主要处理用户的登录逻辑，建议在并发量不大的场景下，将线程数设置为1即可。\n#threads如果设置为0，则使用默认值：CPU核心数，如果queues设置为0，则使用默认值512\nwebsocket.executor.threads=1\nwebsocket.executor.queues=256\n\n#线程池设置\n#threads如果设置为0，则使用默认值：CPU核心数*2，如果queues设置为0，则使用默认值512\nthread.pool.threads=2\nthread.pool.queues=256\n\n#数据库连接池配置（私有部署需要配置）\ndb.pool.url=jdbc:mysql://localhost:3306/leo-im1?serverTimezone=GMT%2B8&useSSL=false\ndb.pool.username=root\ndb.pool.password=root\ndb.pool.maxActive=10\ndb.pool.initialSize=1\ndb.pool.maxWait=60000\ndb.pool.minIdle=1\ndb.pool.testOnBorrow=false\ndb.pool.testOnReturn=false\n\n#dao类型\ndao.type=jdbc\n\n#JWT秘钥\njwt.secret=ILoveLeoIM\n#JWT有效期（毫秒），默认15天\njwt.ttl.millis=1296000000\n\n#缓存设置\n#org.leo.im.http.cache包中有基于Java Map的缓存实现，如果有其他缓存实现，请实现CacheManager接口，并修改CacheManagerFactory类，并修改配置。\ncache.type=map\n\n#通知处理器类型\n#可选类型还有queue、redis，如果是集群部署或Http与WebSocket独立部署，需要选择redis方式。\nnotification.type=queue\n#通知缓存长度\nnotification.capacity=1024\n#通知处理器redis配置\nnotification.redis.host=192.168.0.10\nnotification.redis.port=6379\nnotification.redis.timeout=5000\nnotification.redis.password=password\n#最大连接数\nnotification.redis.pool.maxTotal=100\n#最大空闲连接数\nnotification.redis.pool.maxIdle=5\n#获取连接的最大等待时间\nnotification.redis.pool.maxWait=10000\n#获取的连接是否需要校验\nnotification.redis.pool.testOnBorrow=0\n#redis db index\nnotification.redis.db.index=0\n\n#消息频道设置\n#将私聊、群聊、系统消息放到不同的队列中处理，可提高处理速度，但是每个队列对应一个处理线程，建议根据实际负载量与服务器配置做合理设置。\nchannel.private.message=im-message\nchannel.group.message=im-message\nchannel.system.message=im-message\n\n#文件存储类型（local为本地存储，也可以使用对象存储服务，如minio，需实现org.leo.im.http.file.FileStorage接口）\n#file.settings.driver=local\n#文件保存路径\nfile.settings.directory=f:/develop/leo-files"
  },
  {
    "path": "leo-im-starter/src/main/java/org/leo/im/starter/App.java",
    "content": "package org.leo.im.starter;\n\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Set;\n\nimport org.leo.im.api.provider.ServiceFactory;\nimport org.leo.im.api.service.UserService;\nimport org.leo.im.http.HttpServer;\nimport org.leo.im.migration.FlywayMigration;\nimport org.leo.im.service.support.ServiceProxy;\nimport org.leo.im.socket.ChannelsHolder;\nimport org.leo.im.socket.WebSocketServer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Leo IM Starter\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class App {\n    \n    private final static Logger logger = LoggerFactory.getLogger(App.class);\n    \n    public static void main(String[] args) {\n        // 加载配置文件\n        String confHome = System.getProperty(\"conf.home\");\n        if(confHome == null || confHome.trim().isEmpty()) {\n            logger.error(\"请在启动参数中设置配置文件所在路径， -Dconf.home\");\n            return;\n        }\n        \n        // 加载配置文件\n        if(confHome.endsWith(\"/\") || confHome.endsWith(\"\\\\\")) {\n            confHome = confHome.substring(0, confHome.length() - 1);\n        }\n        try {\n            loadConfigFile(confHome);\n        } catch(IOException e) {\n            logger.error(\"加载配置文件失败，{}\", e.getMessage());\n            return;\n        }\n        \n        //初始化数据库\n        FlywayMigration dbMigration = new FlywayMigration();\n        dbMigration.migrate(System.getProperty(\"db.pool.url\"), System.getProperty(\"db.pool.username\"), System.getProperty(\"db.pool.password\"));\n        \n        String serverTypeConfig = System.getProperty(\"server.type\");\n        if(canStartServer(serverTypeConfig, \"http\")) {\n            // 启动Http服务\n            Thread httpServerThread = new Thread(() -> {\n                try {\n                    new HttpServer().start();\n                } catch (InterruptedException e) {\n                    logger.error(\"启动Http Server失败，{}\", e.getMessage());\n                }\n            });\n            httpServerThread.setName(\"httpServerThread\");\n            httpServerThread.start();\n        }\n        if(canStartServer(serverTypeConfig, \"websocket\")) {\n            Runtime.getRuntime().addShutdownHook(new Thread(() -> {\n                logger.info(\"Clean resources...\");\n                try {\n                    cleanResources();\n                    logger.info(\"Clean resources finished\");\n                } catch(Exception e) {\n                    logger.error(e.getMessage());\n                }\n            }));  \n            \n            WebSocketServer wsServer = new WebSocketServer(Integer.getInteger(\"websocket.port\"));\n            wsServer.setBossThreads(Integer.getInteger(\"websocket.boss.threads\"));\n            wsServer.setWorkerThreads(Integer.getInteger(\"websocket.worker.threads\"));\n            // 启动WebSocket服务\n            Thread websocketServerThread = new Thread(() -> {\n                try {\n                    wsServer.start();\n                } catch (InterruptedException e) {\n                    logger.error(\"启动WebSocket Server失败，{}\", e.getMessage());\n                }\n            });\n            websocketServerThread.setName(\"websocketServerThread\");\n            websocketServerThread.start();\n        }\n    }\n    \n    /**\n     * 加载配置文件\n     * @param confHome\n     * @throws IOException\n     */\n    private static void loadConfigFile(String confHome) throws IOException {\n        InputStream is = new FileInputStream(confHome + \"/app.conf\");\n        System.getProperties().load(is);\n    }\n    \n    /**\n     * 是否可以启动指定类型的服务器\n     * @param serverTypeConfig\n     * @param targerServer\n     * @return\n     */\n    private static boolean canStartServer(String serverTypeConfig, String targerServer) {\n        String[] serverTypes = serverTypeConfig.split(\",\");\n        for(String serverType : serverTypes) {\n            if(serverType.equalsIgnoreCase(targerServer)) {\n                return true;\n            }\n        }\n        return false;\n    }\n    \n    /**\n     * 清理资源\n     */\n    private static void cleanResources() {\n        Set<String> userIds = ChannelsHolder.getUserIds();\n        if(userIds != null && userIds.size() > 0) {\n            UserService serviceProxy = ServiceProxy.newProxyInstance(ServiceFactory.createUserService());\n            serviceProxy.batchOffline(userIds);\n        }\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-starter/src/test/java/org/leo/im/starter/AppTest.java",
    "content": "package org.leo.im.starter;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-starter/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: leo-im-http-1.0.jar asm-6.1.jar thumbnailator-0.4.8.jar ne\n tty-all-4.1.24.Final.jar fastjson-1.2.47.jar netty-rest-server-1.0.ja\n r leo-im-api-1.0.jar leo-im-api-provider-1.0.jar leo-im-service-1.0.j\n ar leo-im-store-1.0.jar mysql-connector-java-8.0.11.jar protobuf-java\n -2.6.0.jar druid-1.1.9.jar leo-im-model-1.0.jar leo-im-util-1.0.jar c\n glib-3.2.6.jar ant-1.9.6.jar ant-launcher-1.9.6.jar jjwt-0.9.0.jar ja\n ckson-databind-2.8.9.jar jackson-annotations-2.8.0.jar jackson-core-2\n .8.9.jar leo-im-notification-1.0.jar jedis-2.9.0.jar commons-pool2-2.\n 4.2.jar leo-im-common-1.0.jar leo-im-socket-1.0.jar leo-im-migration-\n 1.0.jar flyway-core-5.1.1.jar slf4j-api-1.7.25.jar logback-classic-1.\n 2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-starter/target/classes/META-INF/maven/org.leo.im/leo-im-starter/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:35 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-starter\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-starter\nartifactId=leo-im-starter\n"
  },
  {
    "path": "leo-im-starter/target/classes/META-INF/maven/org.leo.im/leo-im-starter/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-starter</artifactId>\n\t<name>leo-im-starter</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-http</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-socket</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-migration</artifactId>\n            <version>${parent.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-starter/target/leo-im-starter-1.0/bin/run.bat",
    "content": "@REM app launcher script\n@REM\n@REM Environment:\n@REM JAVA_HOME - location of a JDK home dir (optional if java on path)\n@setlocal enabledelayedexpansion\n\n@echo off\n\ncd %~dp0\ncd ../\n\nif \"%JAVA_OPS%\" == \"\" set JAVA_OPS=-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -server -Xmx128m -Xms128m -Xss256k\njava %JAVA_OPS% -Dconf.home=%cd%\\conf\\ -jar %cd%\\lib\\leo-im-starter-1.0.jar"
  },
  {
    "path": "leo-im-starter/target/leo-im-starter-1.0/bin/run.sh",
    "content": "#!/bin/sh\n\n###  ------------------------------- ###\n###  leo-im-server launcher script   ###\n###  ------------------------------- ###\n\ncd `dirname $0`\ncd ../\nif [ -z \"$JAVA_OPS\" ]; then\n  JAVA_OPS=\"-Dfile.encoding=utf-8 -Dio.netty.noUnsafe=true -Xms128M -Xmx128M -Xss256K\"\nfi\njava $JAVA_OPS -Dconf.home=$(pwd)/conf/ -jar  $(pwd)/lib/leo-im-starter-1.0.jar"
  },
  {
    "path": "leo-im-starter/target/leo-im-starter-1.0/conf/app.conf",
    "content": "#服务器类别\n#可选项有http，websocket\nserver.type=http,websocket\n\n#Web前端地址\nhttp.origin=http://localhost:3006\n#Http服务器监听端口\nhttp.port=2006\n#Http服务器boss线程数与worker线程数\n#建议boss为1，worker为2，可根据服务器配置自行调整\nhttp.boss.threads=1\nhttp.worker.threads=2\n\n#Http业务线程池与业务队列设置\n#Http Executor线程处理的Http请求较多，建议将线程数设置为2以上。\n#threads如果设置为0，则使用默认值：CPU核心数*2，如果queues设置为0，则使用默认值1024\nhttp.executor.threads=2\nhttp.executor.queues=256\n\n#Http最大内容长度（默认 为10M）\nhttp.max.content.length=50\n\n#WebSocket监听端口\nwebsocket.port=2007\n#WebSocket服务器boss线程数与worker线程数\n#建议boss为1，worker为1，可根据服务器配置自行调整\nwebsocket.boss.threads=1\nwebsocket.worker.threads=1\n\n#WebSocket业务线程池与业务队列设置\n#WebSocket的Executor线程主要处理用户的登录逻辑，建议在并发量不大的场景下，将线程数设置为1即可。\n#threads如果设置为0，则使用默认值：CPU核心数，如果queues设置为0，则使用默认值512\nwebsocket.executor.threads=1\nwebsocket.executor.queues=256\n\n#线程池设置\n#threads如果设置为0，则使用默认值：CPU核心数*2，如果queues设置为0，则使用默认值512\nthread.pool.threads=2\nthread.pool.queues=256\n\n#数据库连接池配置（私有部署需要配置）\ndb.pool.url=jdbc:mysql://localhost:3306/leo-im1?serverTimezone=GMT%2B8&useSSL=false\ndb.pool.username=root\ndb.pool.password=root\ndb.pool.maxActive=10\ndb.pool.initialSize=1\ndb.pool.maxWait=60000\ndb.pool.minIdle=1\ndb.pool.testOnBorrow=false\ndb.pool.testOnReturn=false\n\n#dao类型\ndao.type=jdbc\n\n#JWT秘钥\njwt.secret=ILoveLeoIM\n#JWT有效期（毫秒），默认15天\njwt.ttl.millis=1296000000\n\n#缓存设置\n#org.leo.im.http.cache包中有基于Java Map的缓存实现，如果有其他缓存实现，请实现CacheManager接口，并修改CacheManagerFactory类，并修改配置。\ncache.type=map\n\n#通知处理器类型\n#可选类型还有queue、redis，如果是集群部署或Http与WebSocket独立部署，需要选择redis方式。\nnotification.type=queue\n#通知缓存长度\nnotification.capacity=1024\n#通知处理器redis配置\nnotification.redis.host=192.168.0.28\nnotification.redis.port=6379\nnotification.redis.timeout=5000\nnotification.redis.password=justep\n#最大连接数\nnotification.redis.pool.maxTotal=100\n#最大空闲连接数\nnotification.redis.pool.maxIdle=5\n#获取连接的最大等待时间\nnotification.redis.pool.maxWait=10000\n#获取的连接是否需要校验\nnotification.redis.pool.testOnBorrow=0\n#redis db index\nnotification.redis.db.index=0\n\n#消息频道设置\n#将私聊、群聊、系统消息放到不同的队列中处理，可提高处理速度，但是每个队列对应一个处理线程，建议根据实际负载量与服务器配置做合理设置。\nchannel.private.message=im-message\nchannel.group.message=im-message\nchannel.system.message=im-message\n\n#文件存储类型（local为本地存储，也可以使用对象存储服务，如minio，需实现org.leo.im.http.file.FileStorage接口）\n#file.settings.driver=local\n#文件保存路径\nfile.settings.directory=f:/develop/leo-files"
  },
  {
    "path": "leo-im-starter/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:25 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-starter\n"
  },
  {
    "path": "leo-im-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-starter\\src\\main\\java\\org\\leo\\im\\starter\\App.java\n"
  },
  {
    "path": "leo-im-starter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-starter/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-starter\\src\\test\\java\\org\\leo\\im\\starter\\AppTest.java\n"
  },
  {
    "path": "leo-im-starter/target/surefire-reports/TEST-org.leo.im.starter.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.starter.AppTest\" time=\"0.001\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.starter.AppTest\" name=\"testApp\" time=\"0.001\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-starter/target/surefire-reports/org.leo.im.starter.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.starter.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.057 sec\n"
  },
  {
    "path": "leo-im-store/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-store/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-store</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-store/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-store/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-store/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-store/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-store</artifactId>\n\t<name>leo-im-store</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t<version>${mysql-connector-java.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>druid</artifactId>\n\t\t\t<version>${druid.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-model</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-common</artifactId>\n            <version>${parent.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/connection/ConnectionFactory.java",
    "content": "package org.leo.im.store.connection;\n\nimport java.sql.Connection;\n\n/**\n * 数据库连接工厂接口\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic interface ConnectionFactory {\n\t\n\t/**\n\t * 数据库连接ThreadLocal\n\t */\n\tThreadLocal<Connection> threadDbConnection = new ThreadLocal<Connection>();\n\t\n\t/**\n\t * 得到数据库连接\n\t * @return\n\t */\n\tConnection getConnection();\n\t\n\t/**\n\t * 得到数据库连接\n\t * @param connectionString 连接字符串，开发或测试环境下需要传入连接字符串，生产环境下无需传入。\n\t * @return\n\t */\n\tConnection getConnection(String connectionString);\n\t\n\t/**\n\t * 关闭数据库连接\n\t */\n\tvoid closeConnection();\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/connection/ConnectionProvider.java",
    "content": "package org.leo.im.store.connection;\n\nimport java.sql.Connection;\n\n/**\n * 数据库连接提供者类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic final class ConnectionProvider {\n\t\n\t/**\n\t * 得到数据库连接\n\t * @return\n\t */\n\tpublic static Connection getConnection() {\n\t\treturn ConnectionFactory.threadDbConnection.get();\n\t}\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/connection/impl/PoolConnectionFactory.java",
    "content": "package org.leo.im.store.connection.impl;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\nimport org.leo.im.store.connection.ConnectionFactory;\nimport org.leo.im.store.datasource.ConnectionPool;\nimport org.leo.im.store.datasource.impl.DruidConnectionPool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 数据库连接池连接工厂类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic final class PoolConnectionFactory implements ConnectionFactory {\n\t\n\tprivate final static Logger logger = LoggerFactory.getLogger(PoolConnectionFactory.class);\n\t\n\t/**\n\t * 私有构造函数，避免外部创建实例。\n\t */\n\tprivate PoolConnectionFactory() {\n\t\t\n\t}\n\t\n\tprivate static class InstanceHolder {\n\t\tprivate static PoolConnectionFactory instance = new PoolConnectionFactory();\n\t}\n\t\n\t/**\n\t * 得到单例实例\n\t * @return\n\t */\n\tpublic static PoolConnectionFactory getInstance() {\n\t\treturn InstanceHolder.instance;\n\t}\n\t\n\t/**\n\t * 得到数据库连接\n\t * @return\n\t */\n\t@Override\n\tpublic Connection getConnection() {\n\t\treturn getConnection(null);\n\t}\n\n\t/**\n\t * 得到数据库连接\n\t * @param connectionString 连接字符串，开发或测试环境下需要传入连接字符串，生产环境下无需传入。\n\t * @return\n\t */\n\t@Override\n\tpublic Connection getConnection(String connectionString) {\n\t\tConnection conn = threadDbConnection.get();\n\t\tif(conn == null) {\n\t\t\tconn = getConnectionPool().getConnection();\n\t\t\tthreadDbConnection.set(conn);\n\t\t}\n\t\treturn conn;\n\t}\n\n\t/**\n\t * 关闭数据库连接\n\t */\n\t@Override\n\tpublic void closeConnection() {\n\t\tConnection conn = threadDbConnection.get();\n\t\tif(conn != null) {\n\t\t\ttry {\n\t\t\t\tconn.close();\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlogger.error(e.getMessage());\n\t\t\t}\n\t\t\tthreadDbConnection.remove();\n\t\t}\n\t}\n\t\n\t/**\n\t * 得到连接池实例\n\t * @return\n\t */\n\tprivate ConnectionPool getConnectionPool() {\n\t\treturn DruidConnectionPool.getInstance();\n\t}\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/BaseDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.Parameter;\n\n/**\n * dao基类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic interface BaseDAO {\n    \n    String CONNECTION_NOT_FOUND_EXCEPTION = \"Connection Not Found\";\n\n    /**\n     * 创建id\n     * \n     * @return\n     */\n    default String createId() {\n        return UUID.randomUUID().toString().replace(\"-\", \"\");\n    }\n\n    /**\n     * 设置PreparedStatement的参数\n     * \n     * @param stmt\n     * @param parameters\n     * @throws SQLException\n     * @throws NumberFormatException\n     */\n    default void setPreparedStatmentParameters(PreparedStatement stmt, List<Parameter> parameters)\n            throws NumberFormatException, SQLException {\n        if (parameters != null) {\n            for (int i = 0; i < parameters.size(); i++) {\n                switch (parameters.get(i).getDataType()) {\n                case SHORT:\n                    stmt.setShort(i + 1, Short.parseShort(parameters.get(i).getValue().toString()));\n                    break;\n                case INT:\n                    stmt.setInt(i + 1, Integer.parseInt(parameters.get(i).getValue().toString()));\n                    break;\n                case LONG:\n                    stmt.setLong(i + 1, Long.parseLong(parameters.get(i).getValue().toString()));\n                    break;\n                case FLOAT:\n                    stmt.setFloat(i + 1, Float.parseFloat(parameters.get(i).getValue().toString()));\n                    break;\n                case DOUBLE:\n                    stmt.setDouble(i + 1, Double.parseDouble(parameters.get(i).getValue().toString()));\n                    break;\n                case STRING:\n                    stmt.setString(i + 1, parameters.get(i).getValue().toString());\n                    break;\n                case DATE:\n                    SimpleDateFormat dateFormatter = new SimpleDateFormat(\"yyyy-MM-dd\");\n                    try {\n                        Date formattedDate = dateFormatter.parse(parameters.get(i).getValue().toString());\n                        stmt.setDate(i + 1, new java.sql.Date(formattedDate.getTime()));\n                    } catch (ParseException e) {\n                        throw new DAOException(e);\n                    }\n                    break;\n                case DATETIME:\n                    Date date = (Date) parameters.get(i).getValue();\n                    stmt.setDate(i + 1, new java.sql.Date(date.getTime()));\n                    break;\n                case TIMESTAMP:\n                    Date dt = (Date) parameters.get(i).getValue();\n                    SimpleDateFormat timestampFormatter = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss.SSS\");\n                    stmt.setTimestamp(i + 1, java.sql.Timestamp.valueOf(timestampFormatter.format(dt)));\n                    break;\n                case BOOLEAN:\n                    stmt.setBoolean(i + 1, Boolean.parseBoolean(parameters.get(i).getValue().toString()));\n                    break;\n                case OBJECT:\n                    stmt.setObject(i + 1, parameters.get(i));\n                    break;\n                default:\n                    stmt.setObject(i + 1, parameters.get(i));\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/ChannelDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.leo.im.model.Channel;\n\n/**\n * 频道dao接口\n * \n * @author Leo\n * @date 2018/4/10\n */\npublic interface ChannelDAO {\n    \n    /**\n     * 得到频道列表\n     * @param parameters\n     * @param limit\n     * @return\n     */\n    List<Channel> list(Map<String, Object> parameters, int limit);\n    \n    /**\n     * 得到群组频道\n     * @param parameters\n     * @param types\n     * @param limit\n     * @return\n     */\n    List<Channel> listGroupChannel(Map<String, Object> parameters, String[] types, int limit);\n    \n    /**\n     * 添加频道\n     * @param channel\n     * @return\n     */\n    Channel save(Channel channel);\n    \n    /**\n     * 根据from和to得到频道\n     * @param fromUserId\n     * @param toUserId\n     * @return\n     */\n    Channel getByFromAndTo(String fromUserId, String toUserId);\n    \n    /**\n     * 根据频道id查找频道信息\n     * @param id\n     * @return\n     */\n    Channel getById(String id);\n    \n    /**\n     * 更新最后发送消息的时间\n     * @param id\n     * @param lastPostAt\n     * @return\n     */\n    int updateLastPostAt(String id, long lastPostAt);\n    \n    /**\n     * 增加成员数量\n     * @param id\n     * @param count\n     * @return\n     */\n    int increaseMemberCount(String id, int count);\n    \n    /**\n     * 更新字符字段\n     * @param id\n     * @param field\n     * @param value\n     * @return\n     */\n    int updateStringField(String id, String field, String value);\n    \n    /**\n     * 更新整型字段\n     * @param id\n     * @param field\n     * @param value\n     * @return\n     */\n    int updateIntegerField(String id, String field, int value);\n    \n    /**\n     * 删除频道\n     * @param id\n     * @return\n     */\n    int remove(String id);\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/ChannelMemberDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.util.List;\n\nimport org.leo.im.common.data.Page;\nimport org.leo.im.model.ChannelMember;\n\n/**\n * 频道成员dao接口\n * \n * @author Leo\n * @date 2018/4/10\n */\npublic interface ChannelMemberDAO {\n\n    /**\n     * 添加成员\n     * @param channelId\n     * @param members\n     * @return\n     */\n    int save(String channelId, List<ChannelMember> members);\n    \n    /**\n     * 得到成员列表\n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<ChannelMember> listMember(String channelId, String username, int limit, int offset);\n    \n    /**\n     * 得到用户是否为频道管理员\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    boolean isAdmin(String userId, String channelId);\n    \n    /**\n     * 移除成员\n     * @param channelId\n     * @param memberId\n     * @return\n     */\n    int removeMember(String channelId, String memberId);\n    \n    /**\n     * 修改管理员\n     * @param channelId\n     * @param memberId\n     * @param isAdmin\n     * @return\n     */\n    int changeAdmin(String channelId, String memberId, boolean isAdmin);\n    \n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/FileDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport org.leo.im.model.File;\n\n/**\n * 文件dao接口\n * \n * @author Leo\n * @date 2018/6/13\n */\npublic interface FileDAO {\n\n    /**\n     * 添加文件\n     * @param file\n     * @return\n     */\n    String save(File file);\n    \n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/HideChannelDAO.java",
    "content": "package org.leo.im.store.dao;\n\n/**\n * 隐藏频道dao接口\n * \n * @author Leo\n * @date 2018/6/13\n */\npublic interface HideChannelDAO {\n    \n    /**\n     * 添加隐藏频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    int save(String userId, String channelId);\n    \n    /**\n     * 删除隐藏频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    int remove(String userId, String channelId);\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/MessageDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.util.List;\n\nimport org.leo.im.model.Message;\n\n/**\n * 消息dao接口\n * \n * @author Leo\n * @date 2018/4/21\n */\npublic interface MessageDAO {\n    \n    /**\n     * 得到消息列表\n     * @param channelId\n     * @param maxCreateAt\n     * @param limit\n     * @return\n     */\n    List<Message> listMessage(String channelId, long maxCreateAt, int limit);\n    \n    /**\n     * 根据id得到消息\n     * @param id\n     * @return\n     */\n    Message getById(long id);\n    \n    /**\n     * 添加消息\n     * @param message\n     * @return\n     */\n    long save(Message message);\n    \n    /**\n     * 批量添加消息\n     * @param messages\n     * @return\n     */\n    int save(List<Message> messages);\n    \n    /**\n     * 删除消息\n     * @param messageId\n     * @param userId\n     * @return\n     */\n    int remove(long messageId, String userId);\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/UnreadMessageCountDAO.java",
    "content": "package org.leo.im.store.dao;\n\n/**\n * 未读消息数量dao接口\n * \n * @author Leo\n * @date 2018/4/26\n */\npublic interface UnreadMessageCountDAO {\n    \n    /**\n     * 添加未读消息\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int save(String userId, String channelId, short total);\n    \n    /**\n     * 批量添加未读消息\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int batchSave(String[] userIds, String channelId, short total);\n    \n    /**\n     * 更新未读消息数量\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int update(String userId, String channelId, short total);\n    \n    /**\n     * 批量更新未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int batchUpdate(String[] userIds, String channelId, short total);\n    \n    /**\n     * 增加未读消息数\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int increase(String userId, String channelId, short total);\n    \n    /**\n     * 批量增加未读消息数\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int batchIncrease(String[] userIds, String channelId, short total);\n    \n    /**\n     * 增加群组频道的未读消息数\n     * @param channelId\n     * @param exceptiveUserIds\n     * @param total\n     * @return\n     */\n    int increaseGroupChannel(String channelId, String[] exceptiveUserIds, short total);\n    \n    /**\n     * 减少未读消息数量\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    int decrease(String userId, String channelId, short total);\n    \n    /**\n     * 删除未读消息数量\n     * @param channelId\n     * @param userId\n     * @return\n     */\n    int remove(String channelId, String userId);\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/UserChannelDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.util.List;\n\nimport org.leo.im.model.UserChannel;\n\n/**\n * 用户频道dao接口\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic interface UserChannelDAO {\n\n    /**\n     * 根据用户id得到用户频道列表\n     * @param userId\n     * @param type\n     * @param limit\n     * @return\n     */\n    List<UserChannel> listByUserId(String userId, String type, int limit);\n    \n    /**\n     * 添加用户频道\n     * @param userChannel\n     * @return\n     */\n    int save(UserChannel userChannel);\n    \n    /**\n     * 批量添加用户频道\n     * @param userChannels\n     * @return\n     */\n    int batchSave(List<UserChannel> userChannels);\n    \n    /**\n     * 得到用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    UserChannel get(String userId, String channelId);\n    \n    /**\n     * 更新用户频道显示名\n     * @param channelId\n     * @param userId\n     * @param displayName\n     * @return\n     */\n    int updateDisplayName(String channelId, String userId, String displayName);\n    \n    /**\n     * 删除用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    int remove(String userId, String channelId);\n    \n    /**\n     * 根据名称得到用户频道列表\n     * @param userId\n     * @param name\n     * @param type\n     * @return\n     */\n    List<UserChannel> listByName(String userId, String name, String type);\n    \n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/UserDAO.java",
    "content": "package org.leo.im.store.dao;\n\nimport java.util.Set;\n\nimport org.leo.im.common.data.Page;\nimport org.leo.im.model.User;\n\n/**\n * 用户管理dao接口\n * \n * @author Leo\n * @date 2018/4/8\n */\npublic interface UserDAO {\n\n    /**\n     * 添加用户\n     * @param user\n     * @return 用户id\n     */\n    String save(User user);\n    \n    /**\n     * 根据用户名得到用户\n     * @param name\n     * @return\n     */\n    User getByName(String name);\n    \n    /**\n     * 根据id得到用户\n     * @param id\n     * @return\n     */\n    User getById(String id);\n    \n    /**\n     * 根据名称或昵称得到用户列表\n     * @param name\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<User> listByNameOrNickname(String name, int limit, int offset);\n    \n    /**\n     * 更新用户\n     * @param user\n     * @return\n     */\n    int update(User user);\n    \n    /**\n     * 更新用户\n     * @param user\n     * @param updateNullValueField\n     * @return\n     */\n    int update(User user, boolean updateNullValueField);\n    \n    /**\n     * 判断用户名是否已经存在\n     * @param userId\n     * @param username\n     * @return\n     */\n    boolean usernameExists(String userId, String username);\n    \n    /**\n     * 分页查询非组成员\n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    Page<User> listNonMembers(String channelId, String username, int limit, int offset);\n    \n    /**\n     * 批量设置用户下线\n     * @param userIds\n     * @return\n     */\n    int offline(Set<String> userIds);\n    \n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcChannelDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\nimport org.leo.im.model.Channel;\nimport org.leo.im.model.User;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.ChannelDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.Parameter;\nimport org.leo.im.store.support.ParameterDataTypeEnum;\nimport org.leo.im.store.support.SqlBuildResult;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * Channel DAO JDBC实现类\n * \n * @author Leo\n * @date 2018/4/10\n */\npublic final class JdbcChannelDAOImpl implements BaseDAO, ChannelDAO {\n\n    private static final String CHANNEL_TABLE = \"im_channel\";\n    \n    private static final String USER_TABLE = \"im_user\";\n    \n    /**\n     * 得到频道列表\n     * @param parameters\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<Channel> list(Map<String, Object> parameters, int limit) {\n        return this.listChannel(parameters, null, limit);\n    }\n    \n    /**\n     * 得到群组频道\n     * @param parameters\n     * @param types\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<Channel> listGroupChannel(Map<String, Object> parameters, String[] types, int limit) {\n        StringBuilder sql = new StringBuilder(32);\n        if(types != null && types.length > 0) {\n            for(String type : types) {\n                sql.append(\"type='\").append(type).append(\"' OR \");\n            }\n        }\n        if(sql.length() > 0) {\n            sql.setLength(sql.length() - 4);\n            return this.listChannel(parameters, \"(\" + sql.toString() + \")\", limit);\n        }\n        return this.listChannel(parameters, null, limit);\n    }\n\n    /**\n     * 添加频道\n     * \n     * @param channel\n     * @return\n     */\n    @Override\n    public Channel save(Channel channel) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        channel.setId(this.createId());\n        SqlBuildResult sql = \"G\".equalsIgnoreCase(channel.getType()) ? this.buildInsertGroupChannelSql(channel) : this.buildInsertPrivateChannelSql(channel);\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            if(stmt.executeUpdate() > 0) {\n                return channel;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 根据from和to得到频道\n     * @param fromUserId\n     * @param toUserId\n     * @return\n     */\n    @Override\n    public Channel getByFromAndTo(String fromUserId, String toUserId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT c.id,c.creator_id,c.from_user_id,c.to_user_id,c.type,\");\n        sql.append(\"u1.name AS from_user_name,u1.nickname AS from_user_nickname,\");\n        sql.append(\"u2.name AS to_user_name,u2.nickname AS to_user_nickname,u2.online_status AS to_user_online_status \");\n        sql.append(\"FROM \").append(CHANNEL_TABLE).append(\" c INNER JOIN \").append(USER_TABLE).append(\" u1 ON c.from_user_id=u1.id \");\n        sql.append(\"INNER JOIN \").append(USER_TABLE).append(\" u2 ON c.to_user_id=u2.id \");\n        sql.append(\"WHERE (c.from_user_id=? AND c.to_user_id=?) OR (c.from_user_id=? AND c.to_user_id=?)\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, fromUserId);\n            stmt.setString(2, toUserId);\n            stmt.setString(3, toUserId);\n            stmt.setString(4, fromUserId);\n            rs = stmt.executeQuery();\n            while(rs.next()) {\n                Channel channel = new Channel();\n                channel.setId(rs.getString(\"id\"));\n                channel.setType(rs.getString(\"type\"));\n                \n                User creator = new User();\n                creator.setId(rs.getString(\"creator_id\"));\n                channel.setCreator(creator);\n                \n                User fromUser = new User();\n                fromUser.setId(rs.getString(\"from_user_id\"));\n                fromUser.setName(rs.getString(\"from_user_name\"));\n                fromUser.setNickname(rs.getString(\"from_user_nickname\"));\n                channel.setFrom(fromUser);\n                \n                User toUser = new User();\n                toUser.setId(rs.getString(\"to_user_id\"));\n                toUser.setName(rs.getString(\"to_user_name\"));\n                toUser.setNickname(rs.getString(\"to_user_nickname\"));\n                toUser.setOnlineStatus(rs.getString(\"to_user_online_status\"));\n                channel.setTo(toUser);\n                return channel;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n            DbUtils.closeResultSet(rs);\n        }\n    }\n    \n    /**\n     * 根据频道id查找频道信息\n     * @param id\n     * @return\n     */\n    @Override\n    public Channel getById(String id) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT ch.id,ch.name,ch.type,ch.purpose,ch.member_count,ch.creator_id,ch.from_user_id,ch.to_user_id,\");\n        sql.append(\"u1.name AS from_user_name,u1.nickname AS from_user_nickname,u1.online_status AS from_user_online_status,\");\n        sql.append(\"u2.name AS to_user_name,u2.nickname AS to_user_nickname,u2.online_status AS to_user_online_status FROM \");\n        sql.append(CHANNEL_TABLE).append(\" ch LEFT JOIN im_user u1 ON ch.from_user_id=u1.id \");\n        sql.append(\"LEFT JOIN im_user u2 ON ch.to_user_id=u2.id\");\n        sql.append(\" WHERE ch.id=?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, id);\n            rs = stmt.executeQuery();\n            while(rs.next()) {\n                Channel channel = new Channel();\n                channel.setId(id);\n                channel.setName(rs.getString(\"name\"));\n                channel.setType(rs.getString(\"type\"));\n                channel.setPurpose(rs.getString(\"purpose\"));\n                channel.setMemberCount(rs.getInt(\"member_count\"));\n                \n                User creator = new User();\n                creator.setId(rs.getString(\"creator_id\"));\n                channel.setCreator(creator);\n                \n                if(rs.getString(\"from_user_id\") != null) {\n                    User fromUser = new User();\n                    fromUser.setId(rs.getString(\"from_user_id\"));\n                    fromUser.setName(rs.getString(\"from_user_name\"));\n                    fromUser.setNickname(rs.getString(\"from_user_nickname\"));\n                    fromUser.setOnlineStatus(rs.getString(\"from_user_online_status\"));\n                    channel.setFrom(fromUser);\n                }\n                \n                if(rs.getString(\"to_user_id\") != null) {\n                    User toUser = new User();\n                    toUser.setId(rs.getString(\"to_user_id\"));\n                    toUser.setName(rs.getString(\"to_user_name\"));\n                    toUser.setNickname(rs.getString(\"to_user_nickname\"));\n                    toUser.setOnlineStatus(rs.getString(\"to_user_online_status\"));\n                    channel.setTo(toUser);\n                }\n                return channel;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n            DbUtils.closeResultSet(rs);\n        }\n    }\n    \n    /**\n     * 更新最后发送消息的时间\n     * @param id\n     * @param lastPostAt\n     * @return\n     */\n    public int updateLastPostAt(String id, long lastPostAt) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(CHANNEL_TABLE).append(\" SET last_post_at=? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setLong(1, lastPostAt);\n            stmt.setString(2, id);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 增加成员数量\n     * @param id\n     * @param count\n     * @return\n     */\n    public int increaseMemberCount(String id, int count) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(CHANNEL_TABLE).append(\" SET member_count=member_count+? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setInt(1, count);\n            stmt.setString(2, id);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 更新字符字段\n     * @param id\n     * @param field\n     * @param value\n     * @return\n     */\n    @Override\n    public int updateStringField(String id, String field, String value) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(CHANNEL_TABLE).append(\" SET \");\n        sql.append(field).append(\"=? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, value);\n            stmt.setString(2, id);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 更新整型字段\n     * @param id\n     * @param field\n     * @param value\n     * @return\n     */\n    @Override\n    public int updateIntegerField(String id, String field, int value) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(CHANNEL_TABLE).append(\" SET \");\n        sql.append(field).append(\"=? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setInt(1, value);\n            stmt.setString(2, id);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 删除频道\n     * @param id\n     * @return\n     */\n    @Override\n    public int remove(String id) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        long deleteAt = new java.util.Date().getTime();\n        sql.append(\"UPDATE \").append(CHANNEL_TABLE).append(\" SET delete_at=? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setLong(1, deleteAt);\n            stmt.setString(2, id);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 得到频道列表\n     * @param parameters\n     * @param otherCondition\n     * @param limit\n     * @return\n     */\n    private List<Channel> listChannel(Map<String, Object> parameters, String otherCondition, int limit) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        \n        SqlBuildResult sql = this.buildQuerySql(parameters, otherCondition, limit);\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            rs = stmt.executeQuery();\n            List<Channel> list = new ArrayList<Channel>(64);\n            while (rs.next()) {\n                Channel channel = new Channel();\n                channel.setId(rs.getString(\"id\"));\n                channel.setName(rs.getString(\"name\"));\n                channel.setType(rs.getString(\"type\"));\n                if(rs.getString(\"from_user_id\") != null) {\n                    User from = new User();\n                    from.setId(rs.getString(\"from_user_id\"));\n                    channel.setFrom(from);\n                }\n                if(rs.getString(\"to_user_id\") != null) {\n                    User to = new User();\n                    to.setId(rs.getString(\"to_user_id\"));\n                    channel.setTo(to);\n                }\n                list.add(channel);\n            }\n            return list;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 构建查询语句\n     * @param parameters\n     * @param otherCondition\n     * @param limit\n     * @return\n     */\n    private SqlBuildResult buildQuerySql(Map<String, Object> parameters, String otherCondition, int limit) {\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder whereSql = new StringBuilder(128);\n        sql.append(\"SELECT id,name,type,from_user_id,to_user_id FROM \").append(CHANNEL_TABLE);\n        List<Parameter> parameterList = new ArrayList<>(64);\n        if(!parameters.isEmpty()) {\n            Set<Entry<String, Object>> entrySet = parameters.entrySet();\n            for(Entry<String, Object> entry : entrySet) {\n                Parameter param = getParameter(entry.getKey(), entry.getValue());\n                if(param != null) {\n                    parameterList.add(param);\n                    whereSql.append(param.getName()).append(\"=? AND \");\n                }\n            }\n        }\n        if(whereSql.length() > 0) {\n            whereSql.setLength(whereSql.length() - 5);\n            sql.append(\" WHERE \").append(whereSql.toString());\n        }\n        if(otherCondition != null && !otherCondition.trim().isEmpty()) {\n            sql.append(whereSql.length() > 0 ? \" AND \" : \" WHERE \");\n            sql.append(otherCondition);\n        }\n        sql.append(\" ORDER BY last_post_at\");\n        if(limit > 0) {\n            sql.append(\" LIMIT \").append(limit);\n        }\n        return new SqlBuildResult(sql.toString(), parameterList);\n    }\n    \n    /**\n     * 得到查询参数\n     * @param name\n     * @param value\n     * @return\n     */\n    private Parameter getParameter(String name, Object value) {\n        if(name.equals(\"deleteAt\")) {\n            return new Parameter(\"delete_at\", ParameterDataTypeEnum.LONG, Long.parseLong(value.toString()));\n        }\n        if(name.equals(\"creatorId\")) {\n            return new Parameter(\"creator_id\", ParameterDataTypeEnum.STRING, value.toString());\n        }\n        if(name.equals(\"type\")) {\n            return new Parameter(\"type\", ParameterDataTypeEnum.STRING, value.toString());\n        }  \n        return null;\n    }\n    \n    /**\n     * 构建插入群聊频道语句\n     * @param channel\n     * @return\n     */\n    private SqlBuildResult buildInsertGroupChannelSql(Channel channel) {\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder fields = new StringBuilder(128);\n        StringBuilder values = new StringBuilder(32);\n        List<Parameter> parameters = new ArrayList<>(16);\n        sql.append(\"INSERT INTO \").append(CHANNEL_TABLE);\n        fields.append(\"id,name,type,create_at,member_count,creator_id\");\n        values.append(\"?,?,?,?,?,?\");\n        parameters.add(new Parameter(\"id\", ParameterDataTypeEnum.STRING, channel.getId()));\n        parameters.add(new Parameter(\"name\", ParameterDataTypeEnum.STRING, channel.getName()));\n        parameters.add(new Parameter(\"type\", ParameterDataTypeEnum.STRING, channel.getType()));\n        parameters.add(new Parameter(\"create_at\", ParameterDataTypeEnum.LONG, System.currentTimeMillis()));\n        parameters.add(new Parameter(\"member_count\", ParameterDataTypeEnum.INT, channel.getMemberCount()));\n        parameters.add(new Parameter(\"creator_id\", ParameterDataTypeEnum.STRING, channel.getCreator().getId()));\n        if(channel.getPurpose() != null && !channel.getPurpose().trim().isEmpty()) {\n            fields.append(\",purpose\");\n            values.append(\",?\");\n            parameters.add(new Parameter(\"purpose\", ParameterDataTypeEnum.STRING, channel.getPurpose().trim()));\n        }\n        if(channel.getFrom() != null && channel.getFrom().getId() != null && !channel.getFrom().getId().trim().isEmpty()) {\n            fields.append(\",from_user_id\");\n            values.append(\",?\");\n            parameters.add(new Parameter(\"from_user_id\", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim()));\n        }\n        if(channel.getTo() != null && channel.getTo().getId() != null && !channel.getTo().getId().trim().isEmpty()) {\n            fields.append(\",to_user_id\");\n            values.append(\",?\");\n            parameters.add(new Parameter(\"to_user_id\", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim()));\n        }\n        sql.append(\"(\").append(fields.toString()).append(\")\").append(\" VALUES(\").append(values.toString()).append(\")\");\n        return new SqlBuildResult(sql.toString(), parameters);\n    }\n    \n    /**\n     * 构建插入私聊频道语句\n     * @param channel\n     * @return\n     */\n    private SqlBuildResult buildInsertPrivateChannelSql(Channel channel) {\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder fields = new StringBuilder(128);\n        StringBuilder values = new StringBuilder(32);\n        List<Parameter> parameters = new ArrayList<>(16);\n        sql.append(\"INSERT INTO \").append(CHANNEL_TABLE);\n        fields.append(\"id,name,type,create_at,member_count,creator_id,from_user_id,to_user_id\");\n        values.append(\" SELECT ?,?,?,?,?,?,?,?\");\n        parameters.add(new Parameter(\"id\", ParameterDataTypeEnum.STRING, channel.getId()));\n        parameters.add(new Parameter(\"name\", ParameterDataTypeEnum.STRING, channel.getName()));\n        parameters.add(new Parameter(\"type\", ParameterDataTypeEnum.STRING, channel.getType()));\n        parameters.add(new Parameter(\"create_at\", ParameterDataTypeEnum.LONG, System.currentTimeMillis()));\n        parameters.add(new Parameter(\"member_count\", ParameterDataTypeEnum.INT, channel.getMembers().size()));\n        parameters.add(new Parameter(\"creator_id\", ParameterDataTypeEnum.STRING, channel.getCreator().getId()));\n        parameters.add(new Parameter(\"from_user_id\", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim()));\n        parameters.add(new Parameter(\"to_user_id\", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim()));\n        if(channel.getPurpose() != null && !channel.getPurpose().trim().isEmpty()) {\n            fields.append(\",purpose\");\n            values.append(\",?\");\n            parameters.add(new Parameter(\"purpose\", ParameterDataTypeEnum.STRING, channel.getPurpose().trim()));\n        }\n        values.append(\" FROM dual \").append(\" WHERE NOT EXISTS(SELECT id FROM \").append(CHANNEL_TABLE);\n        values.append(\" WHERE (from_user_id=? AND to_user_id=?) OR (from_user_id=? AND to_user_id=?))\");\n        parameters.add(new Parameter(\"from_user1\", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim()));\n        parameters.add(new Parameter(\"to_user1\", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim()));\n        parameters.add(new Parameter(\"from_user2\", ParameterDataTypeEnum.STRING, channel.getTo().getId().trim()));\n        parameters.add(new Parameter(\"to_user2\", ParameterDataTypeEnum.STRING, channel.getFrom().getId().trim()));\n        sql.append(\"(\").append(fields.toString()).append(\")\").append(values.toString());\n        return new SqlBuildResult(sql.toString(), parameters);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcChannelMemberDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.common.data.Page;\nimport org.leo.im.model.ChannelMember;\nimport org.leo.im.model.User;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.ChannelMemberDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * 频道成员dao jdbc实现类\n * \n * @author Leo\n * @date 2018/4/11\n */\npublic class JdbcChannelMemberDAOImpl implements BaseDAO, ChannelMemberDAO {\n\n    private static final String CHANNEL_MEMBER_TABLE = \"im_channel_member\";\n\n    private static final String USER_TABLE = \"im_user\";\n\n    /**\n     * 添加成员\n     * \n     * @param channelId\n     * @param members\n     * @return\n     */\n    @Override\n    public int save(String channelId, List<ChannelMember> members) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"INSERT INTO \").append(CHANNEL_MEMBER_TABLE).append(\" VALUES(?,?,?)\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            for (ChannelMember member : members) {\n                stmt.setString(1, channelId);\n                stmt.setString(2, member.getUser().getId());\n                stmt.setShort(3, member.getAdmin() ? (short)1 : 0);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 得到成员列表\n     * \n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    @Override\n    public Page<ChannelMember> listMember(String channelId, String username, int limit, int offset) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n\n        StringBuilder countSql = new StringBuilder(256);\n        StringBuilder querySql = new StringBuilder(256);\n        countSql.append(\"SELECT COUNT(*) FROM \").append(CHANNEL_MEMBER_TABLE).append(\" cm INNER JOIN \");\n        countSql.append(USER_TABLE).append(\" u ON cm.user_id=u.id WHERE cm.channel_id=?\");\n        if(username != null && !username.trim().equals(\"\")) {\n            countSql.append(\" AND (u.name LIKE ? OR u.nickname LIKE ?)\");\n        }\n        \n        querySql.append(\"SELECT u.id,u.name,u.name_first_letter,u.nickname,u.avatar_url,cm.is_admin FROM \");\n        querySql.append(CHANNEL_MEMBER_TABLE).append(\" cm INNER JOIN \").append(USER_TABLE).append(\" u \");\n        querySql.append(\"ON cm.user_id=u.id WHERE cm.channel_id=?\");\n        if(username != null && !username.trim().equals(\"\")) {\n            querySql.append(\" AND (u.name LIKE ? OR u.nickname LIKE ?)\");\n        }\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(countSql.toString());\n            stmt.setString(1, channelId);\n            if(username != null && !username.trim().equals(\"\")) {\n                stmt.setString(2, \"%\" + username + \"%\");\n                stmt.setString(3, \"%\" + username + \"%\");\n            }\n            rs = stmt.executeQuery();\n            int total = 0;\n            if (rs.next()) {\n                total = rs.getInt(1);\n            }\n            List<ChannelMember> rows = new ArrayList<>(limit);\n            if (total == 0) {\n                return new Page<ChannelMember>(0, rows);\n            }\n\n            stmt.close();\n            stmt = conn.prepareStatement(querySql.toString());\n            stmt.setString(1, channelId);\n            if(username != null && !username.trim().equals(\"\")) {\n                stmt.setString(2, \"%\" + username + \"%\");\n                stmt.setString(3, \"%\" + username + \"%\");\n            }\n            rs.close();\n            rs = stmt.executeQuery();\n            while (rs.next()) {\n                ChannelMember member = new ChannelMember();\n                User user = new User();\n                user.setId(rs.getString(\"id\"));\n                user.setName(rs.getString(\"name\"));\n                user.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n                user.setNickname(rs.getString(\"nickname\"));\n                user.setAvatarUrl(rs.getString(\"avatar_url\"));\n                member.setUser(user);\n                member.setAdmin(rs.getBoolean(\"is_admin\"));\n                rows.add(member);\n            }\n            return new Page<ChannelMember>(total, rows);\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 得到用户是否为频道管理员\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public boolean isAdmin(String userId, String channelId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        \n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT is_admin FROM \").append(CHANNEL_MEMBER_TABLE);\n        sql.append(\" WHERE user_id=? AND channel_id=?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, channelId);\n            rs = stmt.executeQuery();\n            while(rs.next()) {\n                return rs.getShort(\"is_admin\") == 1;\n            }\n            return false;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 移除成员\n     * @param channelId\n     * @param memberId\n     * @return\n     */\n    public int removeMember(String channelId, String memberId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(128);\n        sql.append(\"DELETE FROM \").append(CHANNEL_MEMBER_TABLE).append(\" WHERE channel_id=? AND user_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, channelId);\n            stmt.setString(2, memberId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 修改管理员\n     * @param channelId\n     * @param memberId\n     * @param isAdmin\n     * @return\n     */\n    @Override\n    public int changeAdmin(String channelId, String memberId, boolean isAdmin) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        \n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(CHANNEL_MEMBER_TABLE).append(\" SET is_admin=? WHERE channel_id=? AND user_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setShort(1, (short)(isAdmin ? 1 : 0));\n            stmt.setString(2, channelId);\n            stmt.setString(3, memberId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcFileDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\n\nimport org.leo.im.model.File;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.FileDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * 文件dao jdbc实现类\n * \n * @author Leo\n * @date 2018/6/13\n */\npublic class JdbcFileDAOImpl implements BaseDAO, FileDAO {\n    \n    private final static String FILE_TABLE = \"im_file\";\n\n    /**\n     * 添加文件\n     * @param file\n     * @return\n     */\n    @Override\n    public String save(File file) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"INSERT INTO \").append(FILE_TABLE).append(\"(id,name,extension,size,mime_typ,width,height,path,thumb_width,thumb_height) \");\n        sql.append(\"VALUES(?,?,?,?,?,?,?,?,?,?)\");\n        \n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            String fileId = createId();\n            stmt.setString(1, fileId);\n            stmt.setString(2, file.getName());\n            stmt.setString(3, file.getExtension());\n            stmt.setInt(4, file.getSize());\n            stmt.setString(5,file.getMimeType());\n            stmt.setInt(6, file.getWidth());\n            stmt.setInt(7, file.getHeight());\n            stmt.setString(8, file.getPath());\n            stmt.setShort(9, file.getThumbWidth());\n            stmt.setShort(10, file.getThumbHeight());\n            if(stmt.executeUpdate() > 0) {\n                return fileId;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcHideChannelDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\n\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.HideChannelDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.util.DbUtils;\n\npublic class JdbcHideChannelDAOImpl implements BaseDAO, HideChannelDAO {\n    \n    private static final String HIDE_CHANNEL_TABLE = \"im_hide_channel\";\n\n    /**\n     * 添加隐藏频道\n     * @param userId\n     * @param channelId\n     */\n    @Override\n    public int save(String userId, String channelId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(64);\n        sql.append(\"INSERT INTO \").append(HIDE_CHANNEL_TABLE).append(\"(user_id,channel_id) VALUES(?,?)\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, channelId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 删除隐藏频道\n     * @param userId\n     * @param channelId\n     */\n    @Override\n    public int remove(String userId, String channelId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(64);\n        sql.append(\"DELETE FROM \").append(HIDE_CHANNEL_TABLE).append(\" WHERE user_id=? AND channel_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, channelId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcMessageDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.model.Channel;\nimport org.leo.im.model.File;\nimport org.leo.im.model.Message;\nimport org.leo.im.model.User;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.MessageDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.BatchSqlBuildResult;\nimport org.leo.im.store.support.Parameter;\nimport org.leo.im.store.support.ParameterDataTypeEnum;\nimport org.leo.im.store.support.SqlBuildResult;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * 消息dao jdbc实现类\n * \n * @author Leo\n * @date 2018/4/25\n */\npublic final class JdbcMessageDAOImpl implements BaseDAO, MessageDAO {\n\n    private static final String MESSAGE_TABLE = \"im_message\";\n\n    private static final String USER_TABLE = \"im_user\";\n    \n    private static final String FILE_TABLE = \"im_file\";\n\n    /**\n     * 得到消息列表\n     * \n     * @param channelId\n     * @param maxCreateTime\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<Message> listMessage(String channelId, long maxCreateAt, int limit) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT m.id,m.channel_id,m.sender_id,m.create_at,m.type,m.content,m.file_id,\");\n        sql.append(\"u.name,u.nickname,u.online_status,u.avatar_url,u.name_first_letter,\");\n        sql.append(\"f.name AS file_name,f.extension,f.size,f.mime_typ,f.width,f.height,f.path,f.thumb_width,f.thumb_height \");\n        sql.append(\"FROM \").append(MESSAGE_TABLE).append(\" m INNER JOIN \").append(USER_TABLE).append(\" u \");\n        sql.append(\"ON m.sender_id=u.id LEFT JOIN \").append(FILE_TABLE).append(\" f ON m.file_id=f.id \");\n        sql.append(\"WHERE m.channel_id=? AND delete_at=0\");\n        if (maxCreateAt > 0) {\n            sql.append(\" AND m.create_at<?\");\n        }\n        sql.append(\" ORDER BY m.create_at DESC LIMIT ?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        int parameterIndex = 1;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(parameterIndex, channelId);\n            if (maxCreateAt > 0) {\n                parameterIndex++;\n                stmt.setLong(parameterIndex, maxCreateAt);\n            }\n            parameterIndex++;\n            stmt.setInt(parameterIndex, limit);\n            rs = stmt.executeQuery();\n            return this.getMessageList(rs, limit);\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 根据id得到消息\n     * \n     * @param id\n     * @return\n     */\n    @Override\n    public Message getById(long id) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT m.id,m.channel_id,m.sender_id,m.create_at,m.type,m.content,m.file_id,\");\n        sql.append(\"u.name,u.nickname,u.online_status,u.avatar_url,name_first_letter,\");\n        sql.append(\"f.name AS file_name,f.extension,f.size,f.mime_typ,f.width,f.height,f.path,f.thumb_width,f.thumb_height \");\n        sql.append(\"FROM \").append(MESSAGE_TABLE).append(\" m INNER JOIN \").append(USER_TABLE).append(\" u \");\n        sql.append(\"ON m.sender_id=u.id LEFT JOIN \").append(FILE_TABLE).append(\" f ON m.file_id=f.id \");\n        sql.append(\"WHERE m.id=?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setLong(1, id);\n            rs = stmt.executeQuery();\n            List<Message> list = this.getMessageList(rs, 1);\n            return list.size() > 0 ? list.get(0) : null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 添加消息\n     * \n     * @param message\n     * @return\n     */\n    @Override\n    public long save(Message message) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        long id = System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000);\n        message.setId(id);\n        SqlBuildResult sql = this.buildInsertSql(message);\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            return stmt.executeUpdate() > 0 ? id : 0;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 批量添加消息\n     * @param messages\n     * @return\n     */\n    @Override\n    public int save(List<Message> messages) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        for(Message message : messages) {\n            long id = System.nanoTime() + ((long) (Math.random() * 9 + 1) * 10000);\n            message.setId(id);\n        }\n        BatchSqlBuildResult sql = this.buildBatchInsertSql(messages);\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            for(List<Parameter> ps : sql.getParameters()) {\n                this.setPreparedStatmentParameters(stmt, ps);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 删除消息\n     * @param messageId\n     * @param userId\n     * @return\n     */\n    @Override\n    public int remove(long messageId, String userId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        \n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(MESSAGE_TABLE).append(\" SET delete_at=? WHERE id=? AND sender_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setLong(1, new java.util.Date().getTime());\n            stmt.setLong(2, messageId);\n            stmt.setString(3, userId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 构建insert 语句\n     * \n     * @param message\n     * @return\n     */\n    private SqlBuildResult buildInsertSql(Message message) {\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder fields = new StringBuilder(128);\n        StringBuilder values = new StringBuilder(16);\n        List<Parameter> ps = new ArrayList<>(8);\n        fields.append(\"id,channel_id,sender_id,create_at\");\n        values.append(\"?,?,?,?\");\n        ps.add(new Parameter(\"id\", ParameterDataTypeEnum.LONG, message.getId()));\n        ps.add(new Parameter(\"channel_id\", ParameterDataTypeEnum.STRING, message.getChannel().getId()));\n        ps.add(new Parameter(\"sender_id\", ParameterDataTypeEnum.STRING, message.getSender().getId()));\n        ps.add(new Parameter(\"create_at\", ParameterDataTypeEnum.LONG, message.getCreateAt()));\n\n        if (message.getType() != null && !message.getType().trim().isEmpty()) {\n            fields.append(\",type\");\n            values.append(\",?\");\n            ps.add(new Parameter(\"type\", ParameterDataTypeEnum.STRING, message.getType()));\n        }\n        if (message.getContent() != null && !message.getContent().trim().isEmpty()) {\n            fields.append(\",content\");\n            values.append(\",?\");\n            ps.add(new Parameter(\"content\", ParameterDataTypeEnum.STRING, message.getContent()));\n        }\n        if (message.getFile() != null) {\n            fields.append(\",file_id\");\n            values.append(\",?\");\n            ps.add(new Parameter(\"file_id\", ParameterDataTypeEnum.STRING, message.getFile().getId()));\n        }\n        sql.append(\"INSERT INTO \").append(MESSAGE_TABLE).append(\"(\").append(fields.toString()).append(\") VALUES(\");\n        sql.append(values.toString()).append(\")\");\n        return new SqlBuildResult(sql.toString(), ps);\n    }\n    \n    /**\n     * 构建批量insert语句\n     * @param messages\n     * @return\n     */\n    private BatchSqlBuildResult buildBatchInsertSql(List<Message> messages) {\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder fields = new StringBuilder(128);\n        StringBuilder values = new StringBuilder(16);\n        List<List<Parameter>> pss = new ArrayList<>(messages.size());\n        fields.append(\"id,channel_id,sender_id,create_at\");\n        values.append(\"?,?,?,?\");\n        if (messages.get(0).getType() != null && !messages.get(0).getType().trim().isEmpty()) {\n            fields.append(\",type\");\n            values.append(\",?\");\n        }\n        if (messages.get(0).getContent() != null && !messages.get(0).getContent().trim().isEmpty()) {\n            fields.append(\",content\");\n            values.append(\",?\");\n        }\n        if (messages.get(0).getFile() != null) {\n            fields.append(\",file_id\");\n            values.append(\",?\");\n        }\n        sql.append(\"INSERT INTO \").append(MESSAGE_TABLE).append(\"(\").append(fields.toString()).append(\") VALUES(\");\n        sql.append(values.toString()).append(\")\");\n        \n        for(Message message : messages) {\n            List<Parameter> ps = new ArrayList<>(8);\n            ps.add(new Parameter(\"id\", ParameterDataTypeEnum.LONG, message.getId()));\n            ps.add(new Parameter(\"channel_id\", ParameterDataTypeEnum.STRING, message.getChannel().getId()));\n            ps.add(new Parameter(\"sender_id\", ParameterDataTypeEnum.STRING, message.getSender().getId()));\n            ps.add(new Parameter(\"create_at\", ParameterDataTypeEnum.LONG, message.getCreateAt()));\n            if (message.getType() != null && !message.getType().trim().isEmpty()) {\n                ps.add(new Parameter(\"type\", ParameterDataTypeEnum.STRING, message.getType()));\n            }\n            if (message.getContent() != null && !message.getContent().trim().isEmpty()) {\n                ps.add(new Parameter(\"content\", ParameterDataTypeEnum.STRING, message.getContent()));\n            }\n            if (message.getFile() != null) {\n                ps.add(new Parameter(\"file_id\", ParameterDataTypeEnum.STRING, message.getFile().getId()));\n            }\n            pss.add(ps);\n        }\n        return new BatchSqlBuildResult(sql.toString(), pss);\n    }\n\n    /**\n     * 得到消息列表\n     * \n     * @param rs\n     * @param count\n     * @return\n     * @throws SQLException\n     */\n    private List<Message> getMessageList(ResultSet rs, int count) throws SQLException {\n        List<Message> list = new ArrayList<>(count);\n        while (rs.next()) {\n            Message message = new Message();\n            message.setId(rs.getLong(\"id\"));\n            message.setCreateAt(rs.getLong(\"create_at\"));\n            message.setType(rs.getString(\"type\"));\n            message.setContent(rs.getString(\"content\"));\n\n            if(rs.getString(\"file_id\") != null) {\n                File file = new File();\n                file.setId(rs.getString(\"file_id\"));\n                file.setName(rs.getString(\"file_name\"));\n                file.setExtension(rs.getString(\"extension\"));\n                file.setSize(rs.getInt(\"size\"));\n                file.setMimeType(rs.getString(\"mime_typ\"));\n                file.setWidth(rs.getInt(\"width\"));\n                file.setHeight(rs.getInt(\"height\"));\n                file.setPath(rs.getString(\"path\"));\n                file.setThumbWidth(rs.getShort(\"thumb_width\"));\n                file.setThumbHeight(rs.getShort(\"thumb_height\"));\n                message.setFile(file);\n            }\n\n            Channel channel = new Channel();\n            channel.setId(rs.getString(\"channel_id\"));\n            message.setChannel(channel);\n\n            User sender = new User();\n            sender.setId(rs.getString(\"sender_id\"));\n            sender.setName(rs.getString(\"name\"));\n            sender.setNickname(rs.getString(\"nickname\"));\n            sender.setOnlineStatus(rs.getString(\"online_status\"));\n            sender.setAvatarUrl(rs.getString(\"avatar_url\"));\n            sender.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n            message.setSender(sender);\n\n            list.add(message);\n        }\n        return list;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUnreadMessageCountDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.UnreadMessageCountDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.Parameter;\nimport org.leo.im.store.support.ParameterDataTypeEnum;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * 未读消息数量dao jdbc实现类\n * \n * @author Leo\n * @date 2018/4/26\n */\npublic final class JdbcUnreadMessageCountDAOImpl implements BaseDAO, UnreadMessageCountDAO {\n    \n    private static final String UNREAD_MESSAGE_COUNT_TABLE = \"im_unread_message_count\";\n\n    /**\n     * 添加未读消息\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int save(String userId, String channelId, short total) {\n        return this.batchSave(new String[] { userId }, channelId, total);\n    }\n    \n    /**\n     * 批量添加未读消息\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int batchSave(String[] userIds, String channelId, short total) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        String sql = \"INSERT INTO \" + UNREAD_MESSAGE_COUNT_TABLE + \"(user_id,channel_id,total) VALUES(?,?,?)\";\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            for(String userId : userIds) {\n                stmt.setString(1, userId);\n                stmt.setString(2, channelId);\n                stmt.setShort(3, total);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 更新未读消息数量\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int update(String userId, String channelId, short total) {\n        return batchUpdate(new String[]{ userId }, channelId, total);\n    }\n\n    /**\n     * 批量更新未读消息数量\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int batchUpdate(String[] userIds, String channelId, short total) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        String sql = \"UPDATE \" + UNREAD_MESSAGE_COUNT_TABLE + \" SET total=? WHERE user_id=? AND channel_id=? AND total<99\";\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            for(String userId : userIds) {\n                stmt.setShort(1, total);\n                stmt.setString(2, userId);\n                stmt.setString(3, channelId);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 增加未读消息数\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int increase(String userId, String channelId, short total) {\n        return this.batchIncrease(new String[] { userId }, channelId, total);\n    }\n    \n    /**\n     * 批量增加未读消息数\n     * @param userIds\n     * @param channelId\n     * @param total\n     * @return\n     */\n    @Override\n    public int batchIncrease(String[] userIds, String channelId, short total) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        String sql = \"UPDATE \" + UNREAD_MESSAGE_COUNT_TABLE + \" SET total=total+? WHERE user_id=? AND channel_id=? AND total<99\";\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            for(String userId : userIds) {\n                stmt.setShort(1, total);\n                stmt.setString(2, userId);\n                stmt.setString(3, channelId);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 增加群组频道的未读消息数\n     * @param channelId\n     * @param exceptiveUserIds\n     * @param total\n     * @return\n     */\n    @Override\n    public int increaseGroupChannel(String channelId, String[] exceptiveUserIds, short total) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        List<Parameter> ps = new ArrayList<>(8);\n        sql.append(\"UPDATE \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" SET total=total+? WHERE channel_id=? AND total<99\");\n        ps.add(new Parameter(\"total\", ParameterDataTypeEnum.SHORT, total));\n        ps.add(new Parameter(\"channel_id\", ParameterDataTypeEnum.STRING, channelId));\n        \n        StringBuilder notInBuilder = new StringBuilder(32); \n        if(exceptiveUserIds != null && exceptiveUserIds.length > 0) {\n            for(int i = 0; i < exceptiveUserIds.length; i++) {\n                notInBuilder.append(notInBuilder.length() > 0 ? \",\" : \"\").append(\"?\");\n                ps.add(new Parameter(\"exceptive_user_id\" + i, ParameterDataTypeEnum.STRING, exceptiveUserIds[i]));\n            }\n            sql.append(\" AND user_id NOT IN(\").append(notInBuilder.toString()).append(\")\");\n        }\n        \n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            this.setPreparedStatmentParameters(stmt, ps);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 减少未读消息数量\n     * @param userId\n     * @param channelId\n     * @param total\n     * @return\n     */\n    public int decrease(String userId, String channelId, short total) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"UPDATE \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" SET total=total-? \");\n        sql.append(\"WHERE channel_id=? AND user_id=? AND total>=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, channelId);\n            stmt.setString(2, userId);\n            stmt.setShort(3, total);\n            stmt.setShort(4, total);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 删除未读消息数量\n     * @param channelId\n     * @param userId\n     * @return\n     */\n    @Override\n    public int remove(String channelId, String userId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(128);\n        sql.append(\"DELETE FROM \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" WHERE channel_id=? AND user_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, channelId);\n            stmt.setString(2, userId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUserChannelDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.leo.im.model.Channel;\nimport org.leo.im.model.User;\nimport org.leo.im.model.UserChannel;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.UserChannelDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.Parameter;\nimport org.leo.im.store.support.ParameterDataTypeEnum;\nimport org.leo.im.store.util.DbUtils;\n\n/**\n * 用户频道dao jdbc实现类\n * \n * @author Leo\n * @date 2018/4/20\n */\npublic final class JdbcUserChannelDAOImpl implements BaseDAO, UserChannelDAO {\n    \n    private static final String USER_CHANNEL_TABLE = \"im_user_channel\";\n    \n    private static final String CHANNEL_TABLE = \"im_channel\";\n    \n    private static final String USER_TABLE = \"im_user\";\n    \n    private static final String UNREAD_MESSAGE_COUNT_TABLE = \"im_unread_message_count\";\n    \n    private static final String HIDE_CHANNEL_TABLE = \"im_hide_channel\";\n\n    /**\n     * 根据用户id得到用户频道列表\n     * @param userId\n     * @param type\n     * @param limit\n     * @return\n     */\n    @Override\n    public List<UserChannel> listByUserId(String userId, String type, int limit) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT c.id,c.name,c.type,c.creator_id,uc.display_name,uc.to_user_id,u.online_status,umc.total \");\n        sql.append(\"FROM \").append(CHANNEL_TABLE).append(\" c INNER JOIN \").append(USER_CHANNEL_TABLE);\n        sql.append(\" uc ON c.id=uc.channel_id LEFT JOIN \").append(USER_TABLE);\n        sql.append(\" u ON uc.to_user_id=u.id INNER JOIN \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" umc \");\n        sql.append(\"ON umc.user_id=uc.user_id AND c.id=umc.channel_id \");\n        sql.append(\"WHERE c.delete_at=0 AND uc.user_id=? \");\n        sql.append(\"AND NOT EXISTS(SELECT channel_id FROM \").append(HIDE_CHANNEL_TABLE).append(\" WHERE user_id=uc.user_id AND channel_id=c.id) \");\n        if(type != null && !type.trim().isEmpty()) {\n            sql.append(\" AND c.type=? \");\n        }\n        sql.append(\"ORDER BY c.last_post_at DESC LIMIT ?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            if(type != null && !type.trim().isEmpty()) {\n                stmt.setString(2, type);\n                stmt.setInt(3, limit);   \n            } else {\n                stmt.setInt(2, limit);\n            }\n            rs = stmt.executeQuery();\n            List<UserChannel> list = new ArrayList<>(64);\n            while(rs.next()) {\n                UserChannel userChannel = new UserChannel();\n                Channel channel = new Channel();\n                channel.setId(rs.getString(\"id\"));\n                channel.setName(rs.getString(\"name\"));\n                channel.setType(rs.getString(\"type\"));\n                User creator = new User();\n                creator.setId(rs.getString(\"creator_id\"));\n                channel.setCreator(creator);\n                userChannel.setChannel(channel);\n                userChannel.setDisplayName(rs.getString(\"display_name\"));\n                User toUser = new User();\n                toUser.setId(rs.getString(\"to_user_id\"));\n                toUser.setOnlineStatus(rs.getString(\"online_status\"));\n                userChannel.setToUser(toUser);\n                userChannel.setUnreadMessageCount(rs.getShort(\"total\"));\n                list.add(userChannel);\n            }\n            return list;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 添加用户频道\n     * @param userChannel\n     * @return\n     */\n    @Override\n    public int save(UserChannel userChannel) {\n        List<UserChannel> list = new ArrayList<>(1);\n        list.add(userChannel);\n        return batchSave(list);\n    }\n    \n    /**\n     * 批量添加用户频道\n     * @param userChannels\n     * @return\n     */\n    @Override\n    public int batchSave(List<UserChannel> userChannels) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }        \n        String sql = \"INSERT INTO \" + USER_CHANNEL_TABLE + \"(user_id,channel_id,display_name,to_user_id) \" +\n                \"VALUES(?,?,?,?)\";\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            for(UserChannel userChannel : userChannels) {\n                stmt.setString(1, userChannel.getUser().getId());\n                stmt.setString(2, userChannel.getChannel().getId());\n                stmt.setString(3, userChannel.getDisplayName());\n                stmt.setString(4, userChannel.getToUser() == null ? null : userChannel.getToUser().getId());\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 得到用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public UserChannel get(String userId, String channelId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT c.id,c.name,c.type,c.purpose,c.member_count,uc.to_user_id,c.creator_id,uc.display_name,u.online_status,umc.total \");\n        sql.append(\"FROM \").append(CHANNEL_TABLE).append(\" c INNER JOIN \").append(USER_CHANNEL_TABLE).append(\" uc \");\n        sql.append(\"ON c.id=uc.channel_id LEFT JOIN \").append(USER_TABLE).append(\" u ON uc.to_user_id=u.id \");\n        sql.append(\"INNER JOIN \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" umc ON umc.user_id=uc.user_id AND umc.channel_id=c.id \");\n        sql.append(\"WHERE uc.user_id=? AND uc.channel_id=?\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, channelId);\n            rs = stmt.executeQuery();\n            while(rs.next()) {\n                UserChannel userChannel = new UserChannel();\n                Channel channel = new Channel();\n                channel.setId(rs.getString(\"id\"));\n                channel.setName(rs.getString(\"name\"));\n                channel.setType(rs.getString(\"type\"));\n                channel.setPurpose(rs.getString(\"purpose\"));\n                channel.setMemberCount(rs.getInt(\"member_count\"));\n                User creator = new User();\n                creator.setId(rs.getString(\"creator_id\"));\n                channel.setCreator(creator);\n                userChannel.setChannel(channel);\n                userChannel.setDisplayName(rs.getString(\"display_name\"));                \n                User toUser = new User();\n                toUser.setId(rs.getString(\"to_user_id\"));\n                toUser.setOnlineStatus(rs.getString(\"online_status\"));\n                userChannel.setToUser(toUser);\n                userChannel.setUnreadMessageCount(rs.getShort(\"total\"));\n                return userChannel;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 更新用户频道显示名\n     * @param channelId\n     * @param userId\n     * @param displayName\n     * @return\n     */\n    @Override\n    public int updateDisplayName(String channelId, String userId, String displayName) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        List<Parameter> ps = new ArrayList<>(4);\n        sql.append(\"UPDATE \").append(USER_CHANNEL_TABLE).append(\" SET display_name=? WHERE channel_id=? AND user_id=?\");\n        ps.add(new Parameter(\"display_name\", ParameterDataTypeEnum.STRING, displayName));\n        ps.add(new Parameter(\"channel_id\", ParameterDataTypeEnum.STRING, channelId));\n        ps.add(new Parameter(\"user_id\", ParameterDataTypeEnum.STRING, userId));\n        \n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            this.setPreparedStatmentParameters(stmt, ps);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 删除用户频道\n     * @param userId\n     * @param channelId\n     * @return\n     */\n    @Override\n    public int remove(String userId, String channelId) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(128);\n        sql.append(\"DELETE FROM \").append(USER_CHANNEL_TABLE).append(\" WHERE user_id=? AND channel_id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, channelId);\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 根据名称得到用户频道列表\n     * @param userId\n     * @param name\n     * @param type\n     * @return\n     */\n    public List<UserChannel> listByName(String userId, String name, String type) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(256);\n        sql.append(\"SELECT c.id,c.name,c.type,c.creator_id,uc.display_name,uc.to_user_id,u.online_status,umc.total \");\n        sql.append(\"FROM \").append(CHANNEL_TABLE).append(\" c INNER JOIN \").append(USER_CHANNEL_TABLE);\n        sql.append(\" uc ON c.id=uc.channel_id LEFT JOIN \").append(USER_TABLE);\n        sql.append(\" u ON uc.to_user_id=u.id INNER JOIN \").append(UNREAD_MESSAGE_COUNT_TABLE).append(\" umc \");\n        sql.append(\"ON umc.user_id=uc.user_id AND c.id=umc.channel_id \");\n        sql.append(\"WHERE c.delete_at=0 AND uc.user_id=? AND (c.name LIKE ? OR uc.display_name LIKE ?)\");\n        if(type != null && !type.trim().isEmpty()) {\n            sql.append(\" AND c.type=?\");\n        }\n        sql.append(\" ORDER BY c.last_post_at DESC\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, userId);\n            stmt.setString(2, \"%\" + name + \"%\");\n            stmt.setString(3, \"%\" + name + \"%\");\n            if(type != null && !type.trim().isEmpty()) {\n                stmt.setString(4, type);\n            }\n            rs = stmt.executeQuery();\n            List<UserChannel> list = new ArrayList<>(64);\n            while(rs.next()) {\n                UserChannel userChannel = new UserChannel();\n                Channel channel = new Channel();\n                channel.setId(rs.getString(\"id\"));\n                channel.setName(rs.getString(\"name\"));\n                channel.setType(rs.getString(\"type\"));\n                User creator = new User();\n                creator.setId(rs.getString(\"creator_id\"));\n                channel.setCreator(creator);\n                userChannel.setChannel(channel);\n                userChannel.setDisplayName(rs.getString(\"display_name\"));\n                User toUser = new User();\n                toUser.setId(rs.getString(\"to_user_id\"));\n                toUser.setOnlineStatus(rs.getString(\"online_status\"));\n                userChannel.setToUser(toUser);\n                userChannel.setUnreadMessageCount(rs.getShort(\"total\"));\n                list.add(userChannel);\n            }\n            return list;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/dao/impl/JdbcUserDAOImpl.java",
    "content": "package org.leo.im.store.dao.impl;\n\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.leo.im.common.data.Page;\nimport org.leo.im.model.User;\nimport org.leo.im.store.connection.ConnectionProvider;\nimport org.leo.im.store.dao.BaseDAO;\nimport org.leo.im.store.dao.UserDAO;\nimport org.leo.im.store.exception.DAOException;\nimport org.leo.im.store.support.Parameter;\nimport org.leo.im.store.support.ParameterDataTypeEnum;\nimport org.leo.im.store.support.SqlBuildResult;\nimport org.leo.im.store.util.DbUtils;\nimport org.leo.im.store.util.FirstLetterUtil;\n\n/**\n * 用户dao jdbc实现类\n * \n * @author Leo\n * @date 2018/4/10\n */\npublic class JdbcUserDAOImpl implements BaseDAO, UserDAO {\n\n    private final static String USER_TABLE = \"im_user\";\n    \n    private final static String CHANNEL_MEMBER_TABLE = \"im_channel_member\";\n\n    /**\n     * 添加用户\n     * \n     * @param user\n     * @return 用户id\n     */\n    @Override\n    public String save(User user) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n\n        SqlBuildResult sql = this.buildInsertSql(user);\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            if (stmt.executeUpdate() > 0) {\n                return user.getId();\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 根据用户名得到用户\n     * \n     * @param name\n     * @return\n     */\n    @Override\n    public User getByName(String name) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        String sql = \"SELECT id,nickname,salt,password,name_first_letter,avatar_url FROM \" + \n                USER_TABLE + \" WHERE name=? AND locked=0\";\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            stmt.setString(1, name);\n            rs = stmt.executeQuery();\n            while (rs.next()) {\n                User user = new User();\n                user.setId(rs.getString(\"id\"));\n                user.setName(name);\n                user.setNickname(rs.getString(\"nickname\"));\n                user.setSalt(rs.getString(\"salt\"));\n                user.setPassword(rs.getString(\"password\"));\n                user.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n                user.setAvatarUrl(rs.getString(\"avatar_url\"));\n                return user;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 根据id得到用户\n     * \n     * @param id\n     * @return\n     */\n    @Override\n    public User getById(String id) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        String sql = \"SELECT name,name_first_letter,nickname,avatar_url,online_status FROM \" + USER_TABLE + \" WHERE id=? AND locked=0\";\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql);\n            stmt.setString(1, id);\n            rs = stmt.executeQuery();\n            while (rs.next()) {\n                User user = new User();\n                user.setId(id);\n                user.setName(rs.getString(\"name\"));\n                user.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n                user.setNickname(rs.getString(\"nickname\"));\n                user.setAvatarUrl(rs.getString(\"avatar_url\"));\n                user.setOnlineStatus(rs.getString(\"online_status\"));\n                return user;\n            }\n            return null;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 根据名称或昵称得到用户列表\n     * @param name\n     * @param limit\n     * @param offset\n     * @return\n     */\n    @Override\n    public Page<User> listByNameOrNickname(String name, int limit, int offset) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        SqlBuildResult sql = this.buildPagingQueryResult(name, limit, offset);\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.getCountSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            rs = stmt.executeQuery();\n            int total = 0;\n            if(rs.next()) {\n                total = rs.getInt(1);\n            }\n            List<User> rows = new ArrayList<>(limit);\n            if(total == 0) {\n                return new Page<User>(total, rows);\n            }\n            \n            stmt.close();\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            rs.close();\n            rs = stmt.executeQuery();\n            while (rs.next()) {\n                User user = new User();\n                user.setId(rs.getString(\"id\"));\n                user.setName(rs.getString(\"name\"));\n                user.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n                user.setNickname(rs.getString(\"nickname\"));\n                user.setAvatarUrl(rs.getString(\"avatar_url\"));\n                rows.add(user);\n            }\n            return new Page<User>(total, rows);\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 更新用户\n     * @param user\n     * @return\n     */\n    @Override\n    public int update(User user) {\n        return update(user, true);\n    }\n    \n    /**\n     * 更新用户\n     * @param user\n     * @param updateNullValueField\n     * @return\n     */\n    @Override\n    public int update(User user, boolean updateNullValueField) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        SqlBuildResult sql = this.buildUpdateSql(user, updateNullValueField);\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.getSql());\n            this.setPreparedStatmentParameters(stmt, sql.getParameters());\n            return stmt.executeUpdate();\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 判断用户名是否已经存在\n     * @param userId\n     * @param username\n     * @return\n     */\n    @Override\n    public boolean usernameExists(String userId, String username) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(128);\n        sql.append(\"SELECT id FROM \").append(USER_TABLE).append(\" WHERE name=?\");\n        if(userId != null && !userId.trim().isEmpty()) {\n            sql.append(\" AND id<>?\");\n        }\n        sql.append(\" LIMIT 1\");\n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            stmt.setString(1, username);\n            if(userId != null && !userId.trim().isEmpty()) {\n                stmt.setString(2, userId);\n            }\n            rs = stmt.executeQuery();\n            if(rs != null) {\n                while(rs.next()) {\n                    return true;\n                }\n            }\n            return false;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 分页查询非组成员\n     * @param channelId\n     * @param username\n     * @param limit\n     * @param offset\n     * @return\n     */\n    @Override\n    public Page<User> listNonMembers(String channelId, String username, int limit, int offset) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        \n        StringBuilder countSql = new StringBuilder(256);\n        countSql.append(\"SELECT COUNT(*) FROM \").append(USER_TABLE).append(\" u \");\n        countSql.append(\"WHERE NOT EXISTS(SELECT user_id FROM \").append(CHANNEL_MEMBER_TABLE).append(\" WHERE user_id=u.id AND channel_id=?)\");\n        countSql.append(\" AND u.id<>'00000000000000000000000000000000'\");\n        if(username != null && !username.trim().equals(\"\")) {\n            countSql.append(\" AND (u.name LIKE ? OR u.nickname LIKE ?)\");\n        }\n        \n        StringBuilder querySql = new StringBuilder(256);\n        querySql.append(\"SELECT u.id,u.name,u.name_first_letter,u.nickname,u.avatar_url \");\n        querySql.append(\"FROM im_user u \");\n        querySql.append(\"WHERE NOT EXISTS(SELECT user_id FROM \").append(CHANNEL_MEMBER_TABLE).append(\" WHERE user_id=u.id AND channel_id=?)\");\n        querySql.append(\" AND u.id<>'00000000000000000000000000000000'\");\n        if(username != null && !username.trim().equals(\"\")) {\n            querySql.append(\" AND (u.name LIKE ? OR u.nickname LIKE ?)\");\n        }\n        querySql.append(\" LIMIT ? OFFSET ?\");\n        \n        PreparedStatement stmt = null;\n        ResultSet rs = null;\n        try {\n            stmt = conn.prepareStatement(countSql.toString());\n            stmt.setString(1, channelId);\n            if(username != null && !username.trim().equals(\"\")) {\n                stmt.setString(2, \"%\" + username + \"%\");\n                stmt.setString(3, \"%\" + username + \"%\");\n            }\n            rs = stmt.executeQuery();\n            int total = 0;\n            if (rs.next()) {\n                total = rs.getInt(1);\n            }\n            List<User> rows = new ArrayList<>(limit);\n            if (total == 0) {\n                return new Page<User>(0, rows);\n            }\n\n            stmt.close();\n            stmt = conn.prepareStatement(querySql.toString());\n            int position = 1;\n            stmt.setString(position, channelId);\n            if(username != null && !username.trim().equals(\"\")) {\n                stmt.setString(2, \"%\" + username + \"%\");\n                stmt.setString(3, \"%\" + username + \"%\");\n                position += 2;\n            }\n            position++;\n            stmt.setInt(position, limit);\n            position++;\n            stmt.setInt(position, offset);\n            rs.close();\n            rs = stmt.executeQuery();\n            while (rs.next()) {\n                User user = new User();\n                user.setId(rs.getString(\"id\"));\n                user.setName(rs.getString(\"name\"));\n                user.setFirstLetterOfName(rs.getString(\"name_first_letter\"));\n                user.setNickname(rs.getString(\"nickname\"));\n                user.setAvatarUrl(rs.getString(\"avatar_url\"));\n                rows.add(user);\n            }\n            return new Page<User>(total, rows);\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeResultSet(rs);\n            DbUtils.closeStatement(stmt);\n        }\n    }\n    \n    /**\n     * 批量设置用户下线\n     * @param userIds\n     * @return\n     */\n    @Override\n    public int offline(Set<String> userIds) {\n        Connection conn = ConnectionProvider.getConnection();\n        if (conn == null) {\n            throw new DAOException(CONNECTION_NOT_FOUND_EXCEPTION);\n        }\n        StringBuilder sql = new StringBuilder(128);\n        sql.append(\"UPDATE \").append(USER_TABLE).append(\" SET online_status=? WHERE id=?\");\n        PreparedStatement stmt = null;\n        try {\n            stmt = conn.prepareStatement(sql.toString());\n            for(String userId : userIds) {\n                stmt.setString(1, \"offline\");\n                stmt.setString(2, userId);\n                stmt.addBatch();\n            }\n            return stmt.executeBatch().length;\n        } catch (SQLException e) {\n            throw new DAOException(e);\n        } finally {\n            DbUtils.closeStatement(stmt);\n        }\n    }\n\n    /**\n     * 构建insert语句\n     * \n     * @param user\n     * @return\n     */\n    private SqlBuildResult buildInsertSql(User user) {\n        String id = this.createId();\n        user.setId(id);\n        StringBuilder sql = new StringBuilder(256);\n        StringBuilder values = new StringBuilder(64);\n        sql.append(\"INSERT INTO \").append(USER_TABLE).append(\"(id,name,salt,password,created_at,name_first_letter\");\n        values.append(\"?,?,?,?,?,?\");\n        List<Parameter> ps = new ArrayList<Parameter>();\n        ps.add(new Parameter(\"id\", ParameterDataTypeEnum.STRING, id));\n        ps.add(new Parameter(\"name\", ParameterDataTypeEnum.STRING, user.getName()));\n        ps.add(new Parameter(\"salt\", ParameterDataTypeEnum.STRING, user.getSalt()));\n        ps.add(new Parameter(\"password\", ParameterDataTypeEnum.STRING, user.getPassword()));\n        ps.add(new Parameter(\"created_at\", ParameterDataTypeEnum.DATETIME, new Date()));\n        String firstLetterOfName = FirstLetterUtil.getFirstLetter(user.getNickname() != null && !user.getNickname().trim().isEmpty() ?\n                user.getNickname().trim() : user.getName().trim());\n        ps.add(new Parameter(\"name_first_letter\", ParameterDataTypeEnum.STRING, firstLetterOfName));\n        if (user.getNickname() != null && !user.getNickname().trim().isEmpty()) {\n            sql.append(\",nickname\");\n            values.append(\",?\");\n            ps.add(new Parameter(\"nickname\", ParameterDataTypeEnum.STRING, user.getNickname().trim()));\n        }\n        sql.append(\") VALUES(\").append(values.toString()).append(\")\");\n        return new SqlBuildResult(sql.toString(), ps);\n    }\n    \n    /**\n     * 构建分页查询语句\n     * @param name\n     * @param limit\n     * @param offset\n     * @return\n     */\n    private SqlBuildResult buildPagingQueryResult(String name, int limit, int offset) {\n        StringBuilder countSql = new StringBuilder(256);\n        StringBuilder querySql = new StringBuilder(256);\n        StringBuilder whereSql = new StringBuilder(64);\n        List<Parameter> ps = new ArrayList<>(3);\n        countSql.append(\"SELECT COUNT(*) FROM \").append(USER_TABLE);\n        querySql.append(\"SELECT id,name,name_first_letter,nickname,avatar_url FROM \").append(USER_TABLE);\n        whereSql.append(\" id<>'00000000000000000000000000000000'\");\n        if(name != null && !name.trim().isEmpty()) {\n            whereSql.append(\" AND (name LIKE ? OR nickname LIKE ?)\");\n            ps.add(new Parameter(\"name\", ParameterDataTypeEnum.STRING, \"%\" + name + \"%\"));\n            ps.add(new Parameter(\"nickname\", ParameterDataTypeEnum.STRING, \"%\" + name + \"%\"));\n        }\n        if(whereSql.length() > 0) {\n            countSql.append(\" WHERE \").append(whereSql.toString());\n            querySql.append(\" WHERE \").append(whereSql.toString());\n        }\n        if(limit > 0) {\n            querySql.append(\" LIMIT \").append(limit);\n        }\n        if(offset >= 0) {\n            querySql.append(\" OFFSET \").append(offset);\n        }\n        return new SqlBuildResult(countSql.toString(), querySql.toString(), ps);\n    }\n    \n    /**\n     * 构建更新语句\n     * @param user\n     * @param updateNullValueField\n     * @return\n     */\n    private SqlBuildResult buildUpdateSql(User user, boolean updateNullValueField) {\n        StringBuilder sql = new StringBuilder(256);\n        List<Parameter> ps = new ArrayList<>(16);\n        sql.append(\"UPDATE \").append(USER_TABLE).append(\" SET \");\n        boolean hasUpdateField = false;\n        if((user.getName() != null && !user.getName().trim().isEmpty()) || updateNullValueField) {\n            sql.append(\"name=?\");\n            ps.add(new Parameter(\"name\", ParameterDataTypeEnum.STRING, user.getName()));\n            hasUpdateField = true;\n        }\n        if((user.getNickname() != null && !user.getNickname().trim().isEmpty()) || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"nickname=?\");\n            ps.add(new Parameter(\"nickname\", ParameterDataTypeEnum.STRING, user.getNickname()));\n            hasUpdateField = true;\n        }\n        if(ps.size() > 0) {\n            String firstLetterOfName = FirstLetterUtil.getFirstLetter(user.getNickname() != null && !user.getNickname().trim().isEmpty() ?\n                    user.getNickname().trim() : user.getName().trim());\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"name_first_letter=?\");\n            ps.add(new Parameter(\"name_first_letter\", ParameterDataTypeEnum.STRING, firstLetterOfName));\n            hasUpdateField = true;\n        }\n        if((user.getSalt() != null && !user.getSalt().trim().isEmpty()) || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"salt=?\");\n            ps.add(new Parameter(\"salt\", ParameterDataTypeEnum.STRING, user.getSalt()));\n            hasUpdateField = true;\n        }\n        if((user.getPassword() != null && !user.getPassword().trim().isEmpty()) || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"password=?\");\n            ps.add(new Parameter(\"password\", ParameterDataTypeEnum.STRING, user.getPassword()));\n            hasUpdateField = true;\n        }\n        if(user.getLocked() != null || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"locked=?\");\n            hasUpdateField = true;\n        }\n        if((user.getAvatarUrl() != null && !user.getAvatarUrl().trim().isEmpty()) || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"avatar_url=?\");\n            ps.add(new Parameter(\"avatar_url\", ParameterDataTypeEnum.STRING, user.getAvatarUrl()));\n            hasUpdateField = true;\n        }\n        if(user.getLastPostAt() > 0) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"last_post_at=?\");\n            ps.add(new Parameter(\"last_post_at\", ParameterDataTypeEnum.LONG, user.getLastPostAt()));\n            hasUpdateField = true;\n        }\n        if((user.getOnlineStatus() != null && !user.getOnlineStatus().trim().isEmpty()) || updateNullValueField) {\n            sql.append(hasUpdateField ? \",\" : \"\").append(\"online_status=?\");\n            ps.add(new Parameter(\"online_status\", ParameterDataTypeEnum.STRING, user.getOnlineStatus()));\n        }\n        sql.append(\" WHERE id=?\");\n        ps.add(new Parameter(\"id\", ParameterDataTypeEnum.STRING, user.getId()));\n        return new SqlBuildResult(sql.toString(), ps);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/datasource/ConnectionPool.java",
    "content": "package org.leo.im.store.datasource;\n\nimport java.sql.Connection;\n\n/**\n * 数据库连接池接口\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic interface ConnectionPool {\n\t\n\t/**\n\t * 得到数据库连接\n\t * @return\n\t */\n\tConnection getConnection();\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/datasource/impl/DruidConnectionPool.java",
    "content": "package org.leo.im.store.datasource.impl;\n\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.util.Properties;\n\nimport org.leo.im.store.datasource.ConnectionPool;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.druid.pool.DruidDataSource;\nimport com.alibaba.druid.pool.DruidDataSourceFactory;\n\n/**\n * druid数据库连接池实现类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic class DruidConnectionPool implements ConnectionPool {\n\t\n\tprivate final static Logger logger = LoggerFactory.getLogger(DruidConnectionPool.class);\n\t\n\t// Druid数据源\n\tprivate static DruidDataSource dataSource = null;\n\n\t/**\n\t * 私有构造函数，避免外部创建实例。\n\t */\n\tprivate DruidConnectionPool() {\n\n\t}\n\n\tprivate static class InstanceHolder {\n\t\tstatic {\n\t\t\tProperties druidProps = new Properties();\n\t\t\tProperties sysProps = System.getProperties();\t\t\t\n\t\t\tsysProps.forEach((key, value) -> {\n\t\t\t\tif(key.toString().startsWith(\"db.pool.\")) {\n\t\t\t\t\tdruidProps.put(key.toString().replace(\"db.pool.\", \"\"), value);\n\t\t\t\t}\n\t\t\t});\n\t\t\ttry {\n\t\t\t\tdataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(druidProps);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlogger.error(e.getMessage());\n\t\t\t}\n\t\t}\n\t\tprivate static DruidConnectionPool instance = new DruidConnectionPool();\n\t}\n\n\t/**\n\t * 得到单例实例\n\t * \n\t * @return\n\t */\n\tpublic static DruidConnectionPool getInstance() {\n\t\treturn InstanceHolder.instance;\n\t}\n\n\t/**\n\t * 得到数据库连接\n\t * \n\t * @return\n\t */\n\t@Override\n\tpublic Connection getConnection() {\n\t\ttry {\n\t\t\treturn dataSource == null ? null : dataSource.getConnection();\n\t\t} catch (SQLException e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/exception/DAOException.java",
    "content": "package org.leo.im.store.exception;\n\n/**\n * dao异常类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic final class DAOException extends RuntimeException {\n\n    private static final long serialVersionUID = 4070326969626499752L;\n    \n    public DAOException() {\n    }\n\n    public DAOException(String message) {\n        super(message);\n    }\n\n    public DAOException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n    public DAOException(Throwable cause) {\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/factory/DAOFactory.java",
    "content": "package org.leo.im.store.factory;\n\nimport org.leo.im.store.dao.ChannelDAO;\nimport org.leo.im.store.dao.ChannelMemberDAO;\nimport org.leo.im.store.dao.FileDAO;\nimport org.leo.im.store.dao.HideChannelDAO;\nimport org.leo.im.store.dao.MessageDAO;\nimport org.leo.im.store.dao.UnreadMessageCountDAO;\nimport org.leo.im.store.dao.UserChannelDAO;\nimport org.leo.im.store.dao.UserDAO;\nimport org.leo.im.store.dao.impl.JdbcChannelDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcChannelMemberDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcFileDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcHideChannelDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcMessageDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcUnreadMessageCountDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcUserChannelDAOImpl;\nimport org.leo.im.store.dao.impl.JdbcUserDAOImpl;\n\n/**\n * dao工厂类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic final class DAOFactory {\n\n    /**\n     * 创建UserDAO的实例\n     * @return\n     */\n    public static UserDAO createUserDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcUserDAOImpl();\n        default:\n            return new JdbcUserDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建ChannelDAO的实例\n     * @return\n     */\n    public static ChannelDAO createChannelDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcChannelDAOImpl();\n        default:\n            return new JdbcChannelDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建ChannelMemberDAO实例\n     * @return\n     */\n    public static ChannelMemberDAO createChannelMemberDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcChannelMemberDAOImpl();\n        default:\n            return new JdbcChannelMemberDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建UserChannelDAO的实例\n     * @return\n     */\n    public static UserChannelDAO createUserChannelDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcUserChannelDAOImpl();\n        default:\n            return new JdbcUserChannelDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建UnreadMessageCountDAO的实例\n     * @return\n     */\n    public static UnreadMessageCountDAO createUnreadMessageCountDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcUnreadMessageCountDAOImpl();\n        default:\n            return new JdbcUnreadMessageCountDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建MessageDAO的实例\n     * @return\n     */\n    public static MessageDAO createMessageDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcMessageDAOImpl();\n        default:\n            return new JdbcMessageDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建HideChannelDAO的实例\n     * @return\n     */\n    public static HideChannelDAO createHideChannelDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcHideChannelDAOImpl();\n        default:\n            return new JdbcHideChannelDAOImpl();\n        }\n    }\n    \n    /**\n     * 创建FileDAO的实例\n     * @return\n     */\n    public static FileDAO createFileDAO() {\n        switch (System.getProperty(\"dao.type\")) {\n        case \"jdbc\":\n            return new JdbcFileDAOImpl();\n        default:\n            return new JdbcFileDAOImpl();\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/support/BatchSqlBuildResult.java",
    "content": "package org.leo.im.store.support;\n\nimport java.util.List;\n\n/**\n * 批量SQL构建结果类\n * \n * @author Leo\n * @date 2018/5/29\n */\npublic final class BatchSqlBuildResult {\n    \n    private String sql;\n    \n    private List<List<Parameter>> parameters;\n    \n    public BatchSqlBuildResult(String sql, List<List<Parameter>> parameters) {\n        this.sql = sql;\n        this.parameters = parameters;\n    }\n    \n    public String getSql() {\n        return this.sql;\n    }\n    \n    public List<List<Parameter>> getParameters() {\n        return this.parameters;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/support/Parameter.java",
    "content": "package org.leo.im.store.support;\n\n/**\n * 数据库参数类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic final class Parameter {\n\nprivate String name;\n    \n    private ParameterDataTypeEnum dataType;\n    \n    private Object value;\n    \n    public Parameter(String name, ParameterDataTypeEnum dataType, Object value) {\n        this.name = name;\n        this.dataType = dataType;\n        this.value = value;\n    }\n    \n    public String getName() {\n        return this.name;\n    }\n    public void setName(String name) {\n        this.name = name;\n    }\n    \n    public ParameterDataTypeEnum getDataType() {\n        return this.dataType;\n    }\n    public void setDataType(ParameterDataTypeEnum dataType) {\n        this.dataType = dataType;\n    }\n    \n    public Object getValue() {\n        return this.value;\n    }\n    public void setValue(Object value) {\n        this.value = value;\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/support/ParameterDataTypeEnum.java",
    "content": "package org.leo.im.store.support;\n\n/**\n * 参数数据类型枚举\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic enum ParameterDataTypeEnum {\n    \n    SHORT,\n    INT,\n    LONG,\n    FLOAT,\n    DOUBLE,\n    STRING,\n    DATE,\n    DATETIME,\n    TIMESTAMP,\n    BOOLEAN,\n    OBJECT\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/support/SqlBuildResult.java",
    "content": "package org.leo.im.store.support;\n\nimport java.util.List;\n\n/**\n * SQL构建结果类\n * \n * @author Leo\n * @date 2018/3/20\n */\npublic final class SqlBuildResult {\n    \n    private String countSql;\n    \n    private String sql;\n    \n    private List<Parameter> parameters;\n    \n    public SqlBuildResult(String sql, List<Parameter> parameters) {\n        this.sql = sql;\n        this.parameters = parameters;\n    }\n    \n    public SqlBuildResult(String countSql, String sql, List<Parameter> parameters) {\n        this.countSql = countSql;\n        this.sql = sql;\n        this.parameters = parameters;\n    }\n    \n    public String getCountSql() {\n        return this.countSql;\n    }\n    \n    public String getSql() {\n        return this.sql;\n    }\n    \n    public List<Parameter> getParameters() {\n        return this.parameters;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/util/DbUtils.java",
    "content": "package org.leo.im.store.util;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 数据库工具类\n * \n * @author Leo\n * @date 2018/3/19\n */\npublic class DbUtils {\n\n    private final static Logger logger = LoggerFactory.getLogger(DbUtils.class);\n\n    /**\n     * 关闭ResultSet\n     * \n     * @param rs\n     */\n    public static void closeResultSet(ResultSet rs) {\n        if (rs != null) {\n            try {\n                rs.close();\n            } catch (SQLException e) {\n                logger.error(e.getMessage());\n            }\n        }\n    }\n\n    /**\n     * 关闭Statement\n     * \n     * @param stmt\n     */\n    public static void closeStatement(Statement stmt) {\n        if (stmt != null) {\n            try {\n                stmt.close();\n            } catch (SQLException e) {\n                logger.error(e.getMessage());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/main/java/org/leo/im/store/util/FirstLetterUtil.java",
    "content": "package org.leo.im.store.util;\n\n/**\n * 去字符串首字母工具类\n * \n * @author Leo\n * @date 2018/3/30\n */\npublic class FirstLetterUtil {\n\n    private static int BEGIN = 45217;\n\n    private static int END = 63486;\n\n    // 按照声母表示，这个表是在GB2312中的出现的第一个汉字，也就是说“啊”是代表首字母a的第一个汉字。\n    // i, u, v都不做声母, 自定规则跟随前面的字母\n    private static char[] chartable = { '啊', '芭', '擦', '搭', '蛾', '发', '噶', '哈', '哈', '击', '喀', '垃', '妈', '拿', '哦', '啪',\n            '期', '然', '撒', '塌', '塌', '塌', '挖', '昔', '压', '匝', };\n\n    // 二十六个字母区间对应二十七个端点\n    // GB2312码汉字区间十进制表示\n    private static int[] table = new int[27];\n\n    // 对应首字母区间表\n    private static char[] initialtable = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'h', 'j', 'k', 'l', 'm', 'n', 'o',\n            'p', 'q', 'r', 's', 't', 't', 't', 'w', 'x', 'y', 'z', };\n    // 初始化\n    static {\n        for (int i = 0; i < 26; i++) {\n            table[i] = gbValue(chartable[i]);// 得到GB2312码的首字母区间端点表，十进制。\n        }\n        table[26] = END;// 区间表结尾\n    }\n\n    /**\n     * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法，思路如下：一个个字符读入、判断、输出\n     */\n    // public static String getFirstLetter(String sourceStr) {\n    // String result = \"\";\n    // String str = sourceStr.toLowerCase();\n    // int StrLength = str.length();\n    // int i;\n    // try {\n    // for (i = 0; i < StrLength; i++) {\n    // result += Char2Initial(str.charAt(i));\n    // }\n    // } catch (Exception e) {\n    // result = \"\";\n    // }\n    // return result;\n    // }\n\n    /**\n     * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法，思路如下：一个个字符读入、判断、输出\n     * \n     * @param source\n     * @return\n     */\n    public static String getFirstLetter(String source) {\n        try {\n            return String.valueOf(char2Initial(source.charAt(0)));\n        } catch (Exception e) {\n            return \"\";\n        }\n    }\n\n    /**\n     * 输入字符,得到他的声母,英文字母返回对应的大写字母,其他非简体汉字返回 '0'\n     * \n     * @param ch\n     * @return\n     */\n    private static char char2Initial(char ch) {\n        // 对英文字母的处理：小写字母转换为大写，大写的直接返回\n        if (ch >= 'a' && ch <= 'z') {\n            return ch;\n        }\n        if (ch >= 'A' && ch <= 'Z') {\n            return ch;\n        }\n        // 对非英文字母的处理：转化为首字母，然后判断是否在码表范围内，\n        // 若不是，则直接返回。\n        // 若是，则在码表内的进行判断。\n        int gb = gbValue(ch);// 汉字转换首字母\n        if ((gb < BEGIN) || (gb > END))// 在码表区间之前，直接返回\n        {\n            return ch;\n        }\n        int i;\n        for (i = 0; i < 26; i++) {// 判断匹配码表区间，匹配到就break,判断区间形如“[,)”\n            if ((gb >= table[i]) && (gb < table[i + 1])) {\n                break;\n            }\n        }\n        if (gb == END) {// 补上GB2312区间最右端\n            i = 25;\n        }\n        return initialtable[i]; // 在码表区间中，返回首字母\n    }\n\n    /**\n     * 取出汉字的编码 cn 汉字\n     * \n     * @param ch\n     * @return\n     */\n    private static int gbValue(char ch) {\n        // 将一个汉字（GB2312）转换为十进制表示。\n        String str = new String();\n        str += ch;\n        try {\n            byte[] bytes = str.getBytes(\"GB2312\");\n            if (bytes.length < 2) {\n                return 0;\n            }\n            return (bytes[0] << 8 & 0xff00) + (bytes[1] & 0xff);\n        } catch (Exception e) {\n            return 0;\n        }\n    }\n\n}\n"
  },
  {
    "path": "leo-im-store/src/test/java/org/leo/im/store/AppTest.java",
    "content": "package org.leo.im.store;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-store/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: mysql-connector-java-8.0.11.jar protobuf-java-2.6.0.jar dr\n uid-1.1.9.jar leo-im-model-1.0.jar leo-im-common-1.0.jar slf4j-api-1.\n 7.25.jar logback-classic-1.2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-store/target/classes/META-INF/maven/org.leo.im/leo-im-store/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:13 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-store\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-store\nartifactId=leo-im-store\n"
  },
  {
    "path": "leo-im-store/target/classes/META-INF/maven/org.leo.im/leo-im-store/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-store</artifactId>\n\t<name>leo-im-store</name>\n\t<url>http://maven.apache.org</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t<version>${mysql-connector-java.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>druid</artifactId>\n\t\t\t<version>${druid.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.leo.im</groupId>\n\t\t\t<artifactId>leo-im-model</artifactId>\n\t\t\t<version>${parent.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>org.leo.im</groupId>\n            <artifactId>leo-im-common</artifactId>\n            <version>${parent.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-store/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:13 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-store\n"
  },
  {
    "path": "leo-im-store/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": "org\\leo\\im\\store\\dao\\BaseDAO$1.class\norg\\leo\\im\\store\\connection\\impl\\PoolConnectionFactory$1.class\norg\\leo\\im\\store\\datasource\\impl\\DruidConnectionPool$1.class\n"
  },
  {
    "path": "leo-im-store/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\util\\FirstLetterUtil.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\support\\Parameter.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\ChannelMemberDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\FileDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\UserDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\support\\BatchSqlBuildResult.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\UserChannelDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcFileDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\connection\\ConnectionFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcUserChannelDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcUserDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\connection\\impl\\PoolConnectionFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\support\\ParameterDataTypeEnum.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\connection\\ConnectionProvider.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\ChannelDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\MessageDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\exception\\DAOException.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\HideChannelDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcChannelDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcChannelMemberDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcMessageDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\datasource\\impl\\DruidConnectionPool.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcUnreadMessageCountDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\util\\DbUtils.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\UnreadMessageCountDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\datasource\\ConnectionPool.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\BaseDAO.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\dao\\impl\\JdbcHideChannelDAOImpl.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\factory\\DAOFactory.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\main\\java\\org\\leo\\im\\store\\support\\SqlBuildResult.java\n"
  },
  {
    "path": "leo-im-store/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-store/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-store\\src\\test\\java\\org\\leo\\im\\store\\AppTest.java\n"
  },
  {
    "path": "leo-im-store/target/surefire-reports/TEST-org.leo.im.store.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.store.AppTest\" time=\"0.01\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.store.AppTest\" name=\"testApp\" time=\"0.01\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-store/target/surefire-reports/org.leo.im.store.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.store.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.093 sec\n"
  },
  {
    "path": "leo-im-util/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" output=\"target/classes\" path=\"src/main/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"src\" output=\"target/test-classes\" path=\"src/test/java\">\n\t\t<attributes>\n\t\t\t<attribute name=\"optional\" value=\"true\"/>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER\">\n\t\t<attributes>\n\t\t\t<attribute name=\"maven.pomderived\" value=\"true\"/>\n\t\t</attributes>\n\t</classpathentry>\n\t<classpathentry kind=\"output\" path=\"target/classes\"/>\n</classpath>\n"
  },
  {
    "path": "leo-im-util/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>leo-im-util</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.m2e.core.maven2Builder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t\t<nature>org.eclipse.m2e.core.maven2Nature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "leo-im-util/.settings/org.eclipse.core.resources.prefs",
    "content": "eclipse.preferences.version=1\nencoding//src/main/java=UTF-8\nencoding//src/test/java=UTF-8\nencoding/<project>=UTF-8\n"
  },
  {
    "path": "leo-im-util/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8\norg.eclipse.jdt.core.compiler.compliance=1.8\norg.eclipse.jdt.core.compiler.problem.forbiddenReference=warning\norg.eclipse.jdt.core.compiler.source=1.8\n"
  },
  {
    "path": "leo-im-util/.settings/org.eclipse.m2e.core.prefs",
    "content": "activeProfiles=\neclipse.preferences.version=1\nresolveWorkspaceProjects=true\nversion=1\n"
  },
  {
    "path": "leo-im-util/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-util</artifactId>\n\t<name>leo-im-util</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>cglib</groupId>\n\t\t\t<artifactId>cglib</artifactId>\n\t\t\t<version>${cglib.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt</artifactId>\n            <version>${jjwt.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-util/src/main/java/org/leo/im/util/BeanUtils.java",
    "content": "package org.leo.im.util;\n\nimport net.sf.cglib.beans.BeanCopier;\n\n/**\n * Java Bean 工具类\n * \n * @author Leo\n * @date 2018/3/20\n */\npublic final class BeanUtils {\n\n    /**\n     * 拷贝属性\n     * @param source\n     * @param target\n     */\n    public static void copyProperties(Object source, Object target) {\n        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);\n        copier.copy(source, target, null);\n    }\n    \n}\n"
  },
  {
    "path": "leo-im-util/src/main/java/org/leo/im/util/JwtUtils.java",
    "content": "package org.leo.im.util;\n\nimport java.util.Base64;\nimport java.util.Date;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.JwtBuilder;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.SignatureAlgorithm;\n\n/**\n * Jwt工具类\n * \n * @author Leo\n * @date 2018/3/28\n */\npublic class JwtUtils {\n\n    /**\n     * 生成加密key\n     * \n     * @param secret\n     * @return\n     */\n    private static SecretKey generalKey(String secret) {\n        byte[] encodedKey = Base64.getDecoder().decode(secret);\n        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, \"AES\");\n        return key;\n    }\n\n    /**\n     * 创建jwt\n     * \n     * @param subject\n     * @param secret\n     * @param ttlMillis\n     * @return\n     */\n    public static String createJWT(String subject, String secret, long ttlMillis) {\n        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;\n        long nowMillis = System.currentTimeMillis();\n        Date now = new Date(nowMillis);\n        SecretKey key = generalKey(secret);\n        JwtBuilder builder = Jwts.builder().setId(\"jwt\").setIssuedAt(now).setSubject(subject)\n                .signWith(signatureAlgorithm, key);\n        if (ttlMillis >= 0) {\n            long expMillis = nowMillis + ttlMillis;\n            Date exp = new Date(expMillis);\n            builder.setExpiration(exp);\n        }\n        return builder.compact();\n    }\n\n    /**\n     * 解密jwt\n     * \n     * @param jwt\n     * @param secret\n     * @return\n     * @throws Exception\n     */\n    public static Claims parseJWT(String jwt, String secret) {\n        SecretKey key = generalKey(secret);\n        Claims claims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt).getBody();\n        return claims;\n    }\n\n}\n"
  },
  {
    "path": "leo-im-util/src/test/java/org/leo/im/util/AppTest.java",
    "content": "package org.leo.im.util;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest \n    extends TestCase\n{\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest( String testName )\n    {\n        super( testName );\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite()\n    {\n        return new TestSuite( AppTest.class );\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp()\n    {\n        assertTrue( true );\n    }\n}\n"
  },
  {
    "path": "leo-im-util/target/classes/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nBuilt-By: Administrator\nClass-Path: cglib-3.2.6.jar asm-6.0.jar ant-1.9.6.jar ant-launcher-1.9\n .6.jar jjwt-0.9.0.jar jackson-databind-2.8.9.jar jackson-annotations-\n 2.8.0.jar jackson-core-2.8.9.jar slf4j-api-1.7.25.jar logback-classic\n -1.2.3.jar logback-core-1.2.3.jar\nBuild-Jdk: 1.8.0_131\nCreated-By: Maven Integration for Eclipse\nMain-Class: org.leo.im.starter.App\n\n"
  },
  {
    "path": "leo-im-util/target/classes/META-INF/maven/org.leo.im/leo-im-util/pom.properties",
    "content": "#Generated by Maven Integration for Eclipse\n#Tue Jun 19 09:17:15 CST 2018\nversion=1.0\ngroupId=org.leo.im\nm2e.projectName=leo-im-util\nm2e.projectLocation=F\\:\\\\Develop\\\\open-source\\\\leo-im-server\\\\leo-im-util\nartifactId=leo-im-util\n"
  },
  {
    "path": "leo-im-util/target/classes/META-INF/maven/org.leo.im/leo-im-util/pom.xml",
    "content": "<?xml version=\"1.0\"?>\n<project\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"\n\txmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.leo.im</groupId>\n\t\t<artifactId>leo-im</artifactId>\n\t\t<version>1.0</version>\n\t</parent>\n\t<artifactId>leo-im-util</artifactId>\n\t<name>leo-im-util</name>\n\t<url>http://maven.apache.org</url>\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t</properties>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>cglib</groupId>\n\t\t\t<artifactId>cglib</artifactId>\n\t\t\t<version>${cglib.version}</version>\n\t\t</dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt</artifactId>\n            <version>${jjwt.version}</version>\n        </dependency>\t\t\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "leo-im-util/target/maven-archiver/pom.properties",
    "content": "#Generated by Maven\n#Tue Jun 12 16:14:14 CST 2018\nversion=1.0\ngroupId=org.leo.im\nartifactId=leo-im-util\n"
  },
  {
    "path": "leo-im-util/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-util/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-util\\src\\main\\java\\org\\leo\\im\\util\\BeanUtils.java\nF:\\Develop\\open-source\\leo-im-server\\leo-im-util\\src\\main\\java\\org\\leo\\im\\util\\JwtUtils.java\n"
  },
  {
    "path": "leo-im-util/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst",
    "content": ""
  },
  {
    "path": "leo-im-util/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst",
    "content": "F:\\Develop\\open-source\\leo-im-server\\leo-im-util\\src\\test\\java\\org\\leo\\im\\util\\AppTest.java\n"
  },
  {
    "path": "leo-im-util/target/surefire-reports/TEST-org.leo.im.util.AppTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<testsuite tests=\"1\" failures=\"0\" name=\"org.leo.im.util.AppTest\" time=\"0.002\" errors=\"0\" skipped=\"0\">\n  <properties>\n    <property name=\"java.runtime.name\" value=\"Java(TM) SE Runtime Environment\"/>\n    <property name=\"sun.boot.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\bin\"/>\n    <property name=\"java.vm.version\" value=\"25.131-b11\"/>\n    <property name=\"java.vm.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.multiModuleProjectDirectory\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.vendor.url\" value=\"http://java.oracle.com/\"/>\n    <property name=\"path.separator\" value=\";\"/>\n    <property name=\"guice.disable.misplaced.annotation.check\" value=\"true\"/>\n    <property name=\"java.vm.name\" value=\"Java HotSpot(TM) 64-Bit Server VM\"/>\n    <property name=\"file.encoding.pkg\" value=\"sun.io\"/>\n    <property name=\"user.script\" value=\"\"/>\n    <property name=\"user.country\" value=\"CN\"/>\n    <property name=\"sun.java.launcher\" value=\"SUN_STANDARD\"/>\n    <property name=\"sun.os.patch.level\" value=\"\"/>\n    <property name=\"java.vm.specification.name\" value=\"Java Virtual Machine Specification\"/>\n    <property name=\"user.dir\" value=\"F:\\Develop\\open-source\\leo-im-server\"/>\n    <property name=\"java.runtime.version\" value=\"1.8.0_131-b11\"/>\n    <property name=\"java.awt.graphicsenv\" value=\"sun.awt.Win32GraphicsEnvironment\"/>\n    <property name=\"java.endorsed.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\endorsed\"/>\n    <property name=\"os.arch\" value=\"amd64\"/>\n    <property name=\"java.io.tmpdir\" value=\"C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\\"/>\n    <property name=\"line.separator\" value=\"\n\"/>\n    <property name=\"java.vm.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.variant\" value=\"\"/>\n    <property name=\"os.name\" value=\"Windows 10\"/>\n    <property name=\"classworlds.conf\" value=\"E:\\Eclipse_Workspace\\.metadata\\.plugins\\org.eclipse.m2e.launching\\launches\\m2conf8379869101416676275.tmp\"/>\n    <property name=\"sun.jnu.encoding\" value=\"GBK\"/>\n    <property name=\"java.library.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\bin;C:\\WINDOWS\\Sun\\Java\\bin;C:\\WINDOWS\\system32;C:\\WINDOWS;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin/server;E:/Software/Java/jdk1.8.0_131/bin/../jre/bin;E:/Software/Java/jdk1.8.0_131/bin/../jre/lib/amd64;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;E:\\Software\\Java\\jdk1.8.0_131\\bin;F:\\Software\\python-3.6.1-embed-amd64;E:\\Software\\node-v6.11.1-win-x64;E:\\Software\\nodejs\\;e:\\Software\\Git\\cmd;E:\\Software\\go\\bin;E:\\Software\\TortoiseGit\\bin;e:\\software\\SSH Communications Security\\SSH Secure Shell;E:\\Software\\Microsoft VS Code\\bin;F:\\Develop\\nodejs\\node_global;C:\\Users\\Administrator\\AppData\\Local\\Microsoft\\WindowsApps;C:\\Users\\Administrator\\AppData\\Roaming\\npm;;C:\\Users\\Administrator\\Desktop;;.\"/>\n    <property name=\"java.specification.name\" value=\"Java Platform API Specification\"/>\n    <property name=\"java.class.version\" value=\"52.0\"/>\n    <property name=\"sun.management.compiler\" value=\"HotSpot 64-Bit Tiered Compilers\"/>\n    <property name=\"os.version\" value=\"10.0\"/>\n    <property name=\"user.home\" value=\"C:\\Users\\Administrator\"/>\n    <property name=\"user.timezone\" value=\"Asia/Shanghai\"/>\n    <property name=\"java.awt.printerjob\" value=\"sun.awt.windows.WPrinterJob\"/>\n    <property name=\"java.specification.version\" value=\"1.8\"/>\n    <property name=\"file.encoding\" value=\"GBK\"/>\n    <property name=\"user.name\" value=\"Administrator\"/>\n    <property name=\"java.class.path\" value=\"/C:/Users/Administrator/.p2/pool/plugins/org.eclipse.m2e.maven.runtime_1.7.1.20161104-1803/jars/plexus-classworlds-2.5.2.jar\"/>\n    <property name=\"java.vm.specification.version\" value=\"1.8\"/>\n    <property name=\"sun.arch.data.model\" value=\"64\"/>\n    <property name=\"java.home\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\"/>\n    <property name=\"sun.java.command\" value=\"org.codehaus.plexus.classworlds.launcher.Launcher -B -gs F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml -s F:\\Software\\apache-maven-3.3.9\\conf\\settings.xml package\"/>\n    <property name=\"java.specification.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"user.language\" value=\"zh\"/>\n    <property name=\"awt.toolkit\" value=\"sun.awt.windows.WToolkit\"/>\n    <property name=\"java.vm.info\" value=\"mixed mode\"/>\n    <property name=\"java.version\" value=\"1.8.0_131\"/>\n    <property name=\"java.ext.dirs\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\ext;C:\\WINDOWS\\Sun\\Java\\lib\\ext\"/>\n    <property name=\"sun.boot.class.path\" value=\"E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\sunrsasign.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;E:\\Software\\Java\\jdk1.8.0_131\\jre\\classes\"/>\n    <property name=\"java.vendor\" value=\"Oracle Corporation\"/>\n    <property name=\"maven.home\" value=\"F:\\Develop\\open-source\\leo-im-server\\EMBEDDED\"/>\n    <property name=\"file.separator\" value=\"\\\"/>\n    <property name=\"java.vendor.url.bug\" value=\"http://bugreport.sun.com/bugreport/\"/>\n    <property name=\"sun.cpu.endian\" value=\"little\"/>\n    <property name=\"sun.io.unicode.encoding\" value=\"UnicodeLittle\"/>\n    <property name=\"sun.desktop\" value=\"windows\"/>\n    <property name=\"sun.cpu.isalist\" value=\"amd64\"/>\n  </properties>\n  <testcase classname=\"org.leo.im.util.AppTest\" name=\"testApp\" time=\"0.002\"/>\n</testsuite>"
  },
  {
    "path": "leo-im-util/target/surefire-reports/org.leo.im.util.AppTest.txt",
    "content": "-------------------------------------------------------------------------------\nTest set: org.leo.im.util.AppTest\n-------------------------------------------------------------------------------\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 sec\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<groupId>org.leo.im</groupId>\n\t<artifactId>leo-im</artifactId>\n\t<version>1.0</version>\n\t<packaging>pom</packaging>\n\n\t<name>leo-im-server</name>\n\t<url>http://maven.apache.org</url>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<java.version>1.8</java.version>\n\t\t<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>\n\t\t<maven-jar-plugin.version>3.1.0</maven-jar-plugin.version>\n\t\t<maven-assembly-plugin.version>3.1.0</maven-assembly-plugin.version>\n\t\t<netty.version>4.1.24.Final</netty.version>\n\t\t<jjwt.version>0.9.0</jjwt.version>\n\t\t<fastjson.version>1.2.47</fastjson.version>\n\t\t<cglib.version>3.2.6</cglib.version>\n\t\t<mysql-connector-java.version>8.0.11</mysql-connector-java.version>\n\t\t<druid.version>1.1.9</druid.version>\n\t\t<jedis.version>2.9.0</jedis.version>\n\t\t<flyway-core.version>5.1.1</flyway-core.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t<version>1.7.25</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-classic</artifactId>\n\t\t\t<version>1.2.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>4.12</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\t<build>\n\t\t<plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <version>${maven-jar-plugin.version}</version>\n                <configuration>\n                    <excludes>\n                        <exclude>conf/**</exclude>\n                    </excludes>\n                    <archive>\n                        <manifest>\n                            <addClasspath>true</addClasspath>\n                            <mainClass>org.leo.im.starter.App</mainClass>\n                        </manifest>\n                    </archive>\n                </configuration>\n            </plugin>\t\t\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>${maven-compiler-plugin.version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>${java.version}</source>\n\t\t\t\t\t<target>${java.version}</target>\n\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-assembly-plugin</artifactId>\n                <version>${maven-assembly-plugin.version}</version>\n                <executions>\n                    <execution>\n                        <id>make-assembly</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>single</goal>\n                        </goals>\n                        <configuration>\n                            <!-- not append assembly id in release file name -->\n                            <appendAssemblyId>false</appendAssemblyId>\n                            <archive>\n                                <manifest>\n                                    <mainClass>org.leo.im.starter.App</mainClass>\n                                </manifest>\n                            </archive>\n                            <descriptors>\n                                <descriptor>assemble/package.xml</descriptor>\n                            </descriptors>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\t\t\t\n\t\t</plugins>\n\t</build>\n\t<modules>\n\t\t<module>leo-im-api</module>\n\t\t<module>leo-im-model</module>\n\t\t<module>leo-im-store</module>\n\t\t<module>leo-im-starter</module>\n\t\t<module>leo-im-service</module>\n\t\t<module>leo-im-http</module>\n\t\t<module>leo-im-util</module>\n\t\t<module>leo-im-socket</module>\n\t\t<module>leo-im-notification</module>\n\t\t<module>leo-im-api-provider</module>\n\t\t<module>leo-im-common</module>\n    <module>leo-im-migration</module>\n  </modules>\n</project>"
  }
]