├── talkshutdown.sh
├── packagedev.sh
├── packageproduct.sh
├── src
└── main
│ ├── java
│ └── com
│ │ └── mogujie
│ │ └── ares
│ │ ├── data
│ │ ├── IData.java
│ │ ├── Counter.java
│ │ ├── GroupCounterItem.java
│ │ ├── Relationship.java
│ │ ├── Audio.java
│ │ ├── GroupRelation.java
│ │ ├── GroupMessage.java
│ │ ├── TransmitFile.java
│ │ ├── Department.java
│ │ ├── Group.java
│ │ ├── Message.java
│ │ └── User.java
│ │ ├── extend
│ │ ├── BaseAction.java
│ │ ├── filter
│ │ │ ├── IFilter.java
│ │ │ ├── LoginFilter.java
│ │ │ └── MessageContentFilter.java
│ │ ├── action
│ │ │ ├── ServerConfig.java
│ │ │ ├── Monitor.java
│ │ │ ├── StatisticsContent.java
│ │ │ ├── Login.java
│ │ │ ├── Dashboard.java
│ │ │ ├── DelayUpdateMonitor.java
│ │ │ ├── FileAction.java
│ │ │ ├── DepartAction.java
│ │ │ ├── UserAction.java
│ │ │ ├── Friendship.java
│ │ │ └── MessageCounter.java
│ │ ├── dispatch
│ │ │ └── DefaultRequestDispatcher.java
│ │ ├── ActionContext.java
│ │ ├── ActionHolder.java
│ │ └── RequestParams.java
│ │ ├── lib
│ │ ├── net
│ │ │ ├── Test.java
│ │ │ ├── IDispatcher.java
│ │ │ ├── IMHttpResponse.java
│ │ │ ├── MoguHttpResponse.java
│ │ │ ├── FileManager.java
│ │ │ ├── Packet.java
│ │ │ ├── BinaryMessageHandler.java
│ │ │ ├── FrameBinaryDecoder.java
│ │ │ ├── FrameBinaryEncoder.java
│ │ │ └── DataBuffer.java
│ │ ├── logger
│ │ │ ├── NoneFilteredLoggerFilter.java
│ │ │ ├── ConnectionKeepAliveFilter.java
│ │ │ ├── LoggerFactory.java
│ │ │ └── Logger.java
│ │ └── storage
│ │ │ ├── CachePool.java
│ │ │ └── DBPool.java
│ │ ├── timer
│ │ ├── ConfigureReloadTask.java
│ │ ├── WorkerInfoReloader.java
│ │ └── Timer.java
│ │ ├── model
│ │ ├── ServerConfigModel.java
│ │ ├── LoginModel.java
│ │ ├── DepartModel.java
│ │ ├── StatisticsModel.java
│ │ └── AudioModel.java
│ │ ├── util
│ │ ├── MoguByteUtil.java
│ │ ├── MoguArrayUtil.java
│ │ ├── HttpUtil.java
│ │ └── MoguUtil.java
│ │ ├── manager
│ │ ├── FileManager.java
│ │ ├── ConfigureManager.java
│ │ ├── CacheManager.java
│ │ ├── DBManager.java
│ │ ├── TimerManager.java
│ │ └── ElegantStopManager.java
│ │ ├── configure
│ │ ├── SysConstants.java
│ │ ├── BizConstants.java
│ │ ├── Configure.java
│ │ ├── Router.java
│ │ └── PBRouter.java
│ │ └── MainServer.java
│ └── resources
│ ├── system.properties
│ ├── timer.xml
│ ├── db-online.properties
│ ├── db-dev.properties
│ ├── cache-online.properties
│ ├── cache-dev.properties
│ └── logback.xml
├── run.sh
├── startup.sh
├── README.md
└── pom.xml
/talkshutdown.sh:
--------------------------------------------------------------------------------
1 | echo stop > /tmp/.mogutalk_stop.tmp
2 |
--------------------------------------------------------------------------------
/packagedev.sh:
--------------------------------------------------------------------------------
1 | mvn clean package -Dmaven.test.skip=true
2 | mvn dependency:copy-dependencies
3 |
--------------------------------------------------------------------------------
/packageproduct.sh:
--------------------------------------------------------------------------------
1 | mvn clean package -P product -Dmaven.test.skip=true
2 | mvn dependency:copy-dependencies
3 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/IData.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | public interface IData {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/BaseAction.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend;
2 |
3 | public abstract class BaseAction {
4 | }
5 |
--------------------------------------------------------------------------------
/src/main/resources/system.properties:
--------------------------------------------------------------------------------
1 | mcrypt_salt=tt vs TT ?
2 | task_initialize_pool=3
3 |
4 | com.mogujie.ares.config.file.db=${config.file.db}
5 | com.mogujie.ares.config.file.cache=${config.file.cache}
6 | com.mogujie.ares.config.file.route=route.xml
7 | com.mogujie.ares.config.file.timer=timer.xml
8 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/filter/IFilter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.filter;
2 |
3 | /**
4 | *
5 | * @Description: 请求的过滤器
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2013-7-21 下午3:36:07
8 | *
9 | */
10 | public interface IFilter {
11 |
12 | public void doFilter();
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/resources/timer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | com.mogujie.ares.timer.ConfigureReloadTask
5 | 0
6 | rateTask
7 | 3
8 | 3
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/resources/db-online.properties:
--------------------------------------------------------------------------------
1 | instances=macim_master,macim_slave
2 | macim_master_url=jdbc:mysql://1.1.1.1/ttim
3 | macim_master_port=1
4 | macim_master_username=1
5 | macim_master_password=1
6 | macim_slave_url=jdbc:mysql://1.1.1.1/macim
7 | macim_master_port=1
8 | macim_slave_username=1
9 | macim_slave_password=1
10 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/Test.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | public class Test {
4 |
5 | public static void main(String[] args) {
6 | // TODO Auto-generated method stub
7 | String str = "2345678965435678908765467890";
8 | MoguHttp.uploadAudioByteFile("http://122.225.68.125:8001/", str.getBytes());
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/resources/db-dev.properties:
--------------------------------------------------------------------------------
1 | instances=macim_master,macim_slave
2 | # macim master库
3 | macim_master_url=jdbc:mysql://1.1.1.1/ttim
4 | macim_master_port=1
5 | macim_master_username=1
6 | macim_master_password=1
7 | # macim slave库
8 | macim_slave_url=jdbc:mysql://1.1.1.1/ttim
9 | macim_master_port=1
10 | macim_slave_username=1
11 | macim_slave_password=1
12 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/filter/LoginFilter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.filter;
2 |
3 | /**
4 | *
5 | * @Description: 登陆请求的过滤处理
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2013-7-21 下午3:36:31
8 | *
9 | */
10 | public class LoginFilter implements IFilter {
11 |
12 | @Override
13 | public void doFilter() {
14 |
15 | }
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | java -server -Djava.ext.dirs=./ -Xmx2048M -Xms2048M -Xmn128M -XX:PermSize=64m -XX:MaxPermSize=128m -XX:+UseCompressedOops -XX:+UseParallelOldGC -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails -Xloggc:/tmp/mogutalk-gc.log -XX:+HeapDumpOnOutOfMemoryError -Djava.io.tmpdir=/tmp -XX:HeapDumpPath=/tmp/oom.heapdump -jar ./mogutalk-business-0.0.1-SNAPSHOT.jar $1 > mogutalk.log &
2 | echo $! > ./mogutalk.pid
3 |
--------------------------------------------------------------------------------
/src/main/resources/cache-online.properties:
--------------------------------------------------------------------------------
1 | instances=counter,unread,group_counter,business
2 | counter_host=1.1.1.1
3 | counter_port=1
4 | counter_db=0
5 | #未读消息计数器的redis
6 | unread_host=1.1.1.1
7 | unread_port=1
8 | unread_db=1
9 |
10 | #群消息计数,未读消息和总计数都在这里
11 | group_counter_host=1.1.1.1
12 | group_counter_port=1
13 | group_counter_db=1
14 |
15 | #IM 业务 cache
16 | business_host=1.1.1.1
17 | business_port=1
18 | business_db=1
19 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/filter/MessageContentFilter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.filter;
2 |
3 | /**
4 | *
5 | * @Description: 消息内容相关请求的过滤处理
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2013-7-21 下午3:36:51
8 | *
9 | */
10 | public class MessageContentFilter implements IFilter {
11 |
12 | @Override
13 | public void doFilter() {
14 | // TODO Auto-generated method stub
15 |
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/resources/cache-dev.properties:
--------------------------------------------------------------------------------
1 | instances=counter,unread,group_counter,business
2 | #消息计数器的redis
3 | counter_host=1.1.1.1
4 | counter_port=1
5 | counter_db=0
6 | #未读消息计数器的redis
7 | unread_host=1.1.1.1
8 | unread_port=1
9 | unread_db=1
10 |
11 | #群消息计数,未读消息和总计数都在这里
12 | group_counter_host=1.1.1.1
13 | group_counter_port=1
14 | group_counter_db=1
15 |
16 | #IM 业务 cache
17 | business_host=1.1.1.1
18 | business_port=1
19 | business_db=1
20 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/ServerConfig.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import com.mogujie.ares.extend.BaseAction;
4 | import com.mogujie.ares.lib.net.DataBuffer;
5 | import com.mogujie.ares.model.ServerConfigModel;
6 |
7 | /**
8 | *
9 | * @Description: 处理服务端配置的请求.
10 | * @author zuoye - zuoye[at]mogujie.com
11 | * @date 2013-11-5
12 | *
13 | */
14 | public class ServerConfig extends BaseAction {
15 | }
16 |
--------------------------------------------------------------------------------
/startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | t=`date +%Y%m%d_%H%M%S`
4 | cp src/main/bin/* ./
5 | ALLPROTS=($@)
6 | for port in ${ALLPROTS[@]}
7 | do
8 | file="./${port}"
9 | if [ -d "$file" ] ; then
10 | if [ ! -d "backup" ] ; then
11 | mkdir "backup"
12 | fi
13 | bak="backup/${port}_${t}"
14 | mv $file $bak
15 | fi
16 | mkdir $port
17 | cd $port
18 | cp ../target/mogutalk-business-0.0.1-SNAPSHOT.jar ./
19 | cp ../run.sh ./
20 | sh run.sh $port
21 | cd ../
22 | done
23 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/IDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import org.jboss.netty.channel.ChannelHandlerContext;
4 | import org.jboss.netty.channel.MessageEvent;
5 |
6 | /**
7 | *
8 | * @ClassName: IDispatcher
9 | * @Description: 请求分发的接口
10 | * @author ziye - ziye(at)mogujie.com
11 | * @date 2013-7-20 下午5:55:47
12 | *
13 | */
14 | public interface IDispatcher {
15 |
16 | public void dispatch(ChannelHandlerContext context, MessageEvent e);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/timer/ConfigureReloadTask.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.timer;
2 |
3 | import com.mogujie.ares.lib.logger.Logger;
4 | import com.mogujie.ares.lib.logger.LoggerFactory;
5 |
6 | /**
7 | *
8 | * @Description: 配置实时更新的类
9 | * @author shitou - shitou[at]mogujie.com
10 | * @date 2013-7-22 下午2:54:08
11 | *
12 | */
13 | public class ConfigureReloadTask implements Runnable
14 | {
15 | private Logger logger = LoggerFactory.getLogger(ConfigureReloadTask.class);
16 | @Override
17 | public void run()
18 | {
19 | //重载所有的配置
20 | //ConfigureManager.getInstance().reloadAllConfigs();
21 | logger.info("Hello World");
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/logger/NoneFilteredLoggerFilter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.logger;
2 |
3 | import ch.qos.logback.classic.spi.ILoggingEvent;
4 | import ch.qos.logback.core.filter.Filter;
5 | import ch.qos.logback.core.spi.FilterReply;
6 |
7 | /**
8 | *
9 | * @Description: 为过滤的一些通用日志的过滤器
10 | * @author ziye - ziye[at]mogujie.com
11 | * @date 2013-7-21 下午3:38:53
12 | *
13 | */
14 | public class NoneFilteredLoggerFilter extends Filter {
15 |
16 | @Override
17 | public FilterReply decide(ILoggingEvent event) {
18 |
19 | if (event.getMessage().contains("[LoggerFilter:")) {
20 | return FilterReply.DENY;
21 | } else {
22 | return FilterReply.ACCEPT;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/model/ServerConfigModel.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.model;
2 |
3 | /*
4 | *
5 | * @Description: 服务配置相关操作
6 | * @author zuoye - zuoye[at]mogujie.com
7 | * @date 2013-11-5
8 | *
9 | */
10 | public class ServerConfigModel {
11 |
12 | private static ServerConfigModel configModelInstance;
13 |
14 | public static ServerConfigModel getInstance() {
15 | if (configModelInstance == null) {
16 | configModelInstance = new ServerConfigModel();
17 | }
18 | return configModelInstance;
19 | }
20 |
21 | private ServerConfigModel() {
22 | }
23 |
24 | public String getSensitivityWord() throws Exception {
25 | return "";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/logger/ConnectionKeepAliveFilter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.logger;
2 |
3 | import ch.qos.logback.classic.spi.ILoggingEvent;
4 | import ch.qos.logback.core.filter.Filter;
5 | import ch.qos.logback.core.spi.FilterReply;
6 |
7 | /**
8 | *
9 | * @Description: heartbeat 之类和连接相关的日志过滤器
10 | * @author ziye - ziye[at]mogujie.com
11 | * @date 2013-7-21 下午3:37:45
12 | *
13 | */
14 | public class ConnectionKeepAliveFilter extends Filter {
15 |
16 | @Override
17 | public FilterReply decide(ILoggingEvent event) {
18 |
19 | if (event.getMessage().contains("[LoggerFilter:Connection-Keep-alive]")) {
20 | return FilterReply.ACCEPT;
21 | } else {
22 | return FilterReply.DENY;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/Monitor.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import com.mogujie.ares.extend.BaseAction;
4 | import com.mogujie.ares.lib.logger.Logger;
5 | import com.mogujie.ares.lib.logger.LoggerFactory;
6 | import com.mogujie.ares.lib.net.DataBuffer;
7 |
8 | public class Monitor extends BaseAction {
9 |
10 | @SuppressWarnings("unused")
11 | private static final Logger logger = LoggerFactory.getLogger(Monitor.class);
12 |
13 | /**
14 | *
15 | * @Description: heartbeat
16 | * @param clientAddress
17 | * @return
18 | */
19 | public DataBuffer heartbeat(String clientAddress, int version) {
20 | DataBuffer dataBuffer = new DataBuffer(0);
21 | return dataBuffer; // 不用返回
22 | }
23 | }
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/IMHttpResponse.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | /**
4 | *
5 | * @Description: http返回值
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2014-3-9 下午10:21:08
8 | *
9 | */
10 | public class IMHttpResponse {
11 | private int statusCode;
12 | private String responseBody;
13 |
14 | public int getStatusCode() {
15 | return statusCode;
16 | }
17 |
18 | public void setStatusCode(int statusCode) {
19 | this.statusCode = statusCode;
20 | }
21 |
22 | public String getResponseBody() {
23 | return responseBody;
24 | }
25 |
26 | public void setResponseBody(String responseBody) {
27 | this.responseBody = responseBody;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "IMHttpResponse [statusCode=" + statusCode + ", responseBody="
33 | + responseBody + "]";
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/MoguHttpResponse.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | /**
4 | *
5 | * @Description: http返回值
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2014-3-9 下午10:21:08
8 | *
9 | */
10 | public class MoguHttpResponse {
11 | private int statusCode;
12 | private String responseBody;
13 |
14 | public int getStatusCode() {
15 | return statusCode;
16 | }
17 |
18 | public void setStatusCode(int statusCode) {
19 | this.statusCode = statusCode;
20 | }
21 |
22 | public String getResponseBody() {
23 | return responseBody;
24 | }
25 |
26 | public void setResponseBody(String responseBody) {
27 | this.responseBody = responseBody;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "IMHttpResponse [statusCode=" + statusCode + ", responseBody="
33 | + responseBody + "]";
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/util/MoguByteUtil.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.util;
2 |
3 | public class MoguByteUtil {
4 |
5 | private static MoguByteUtil instance = new MoguByteUtil();
6 |
7 | public static MoguByteUtil getInstance()
8 | {
9 | if(instance == null)
10 | {
11 | synchronized(instance) {
12 | instance = new MoguByteUtil();
13 | }
14 | }
15 | return instance;
16 | }
17 |
18 | public int convert2Int(byte[] bytes) {
19 | return (((int)bytes[0]) << 24) + (((int)bytes[1]) << 16) + (((int)bytes[2]) << 8) + bytes[3];
20 | }
21 |
22 | public byte[] getBytes(int value) {
23 | byte[] bytes = new byte[4];
24 | bytes[0] = (byte)(value >>> 24);//取最高8位放到0下标
25 | bytes[1] = (byte)(value >>> 16);//取次高8为放到1下标
26 | bytes[2] = (byte)(value >>> 8); //取次低8位放到2下标
27 | bytes[3] = (byte)(value ); //取最低8位放到3下标
28 | return bytes;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/logger/LoggerFactory.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.logger;
2 |
3 | import java.util.concurrent.ConcurrentHashMap;
4 | import java.util.concurrent.ConcurrentMap;
5 | // ziye copy from mdl-common
6 | public class LoggerFactory {
7 |
8 | private static final ConcurrentMap loggers = new ConcurrentHashMap();
9 |
10 | public static Logger getLogger(Class> key) {
11 | Logger logger = loggers.get(key.getName());
12 | if (logger == null) {
13 | loggers.putIfAbsent(key.getName(), new Logger(key));
14 | logger = loggers.get(key.getName());
15 | }
16 | return logger;
17 | }
18 |
19 | public static Logger getLogger(String key) {
20 | Logger logger = loggers.get(key);
21 | if (logger == null) {
22 | loggers.putIfAbsent(key, new Logger(key));
23 | logger = loggers.get(key);
24 | }
25 | return logger;
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Counter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | *
7 | * @ClassName: Counter
8 | * @Description: 计数器描述类
9 | * @author shitou - shitou(at)mogujie.com
10 | * @date 2013-7-20 下午5:52:24
11 | *
12 | */
13 | public class Counter
14 | {
15 |
16 | public int userId;
17 |
18 | public Map unreadCount;
19 |
20 | public Map msgCount;
21 |
22 | public void setUserId(int setUserId)
23 | {
24 | userId = setUserId;
25 | }
26 |
27 | public int getUserId()
28 | {
29 | return userId;
30 | }
31 |
32 | public void setUnreadCount(MapunreadMap)
33 | {
34 | unreadCount = unreadMap;
35 | }
36 |
37 | public Map getUnreadCount()
38 | {
39 | return unreadCount;
40 | }
41 |
42 | public void setMsgCount(MapsetMsgCount)
43 | {
44 | msgCount = setMsgCount;
45 | }
46 |
47 | public Map getMsgCount()
48 | {
49 | return msgCount;
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/util/MoguArrayUtil.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.util;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 |
6 | public class MoguArrayUtil {
7 |
8 | private static MoguArrayUtil instance = new MoguArrayUtil();
9 |
10 | public static MoguArrayUtil getInstance()
11 | {
12 | if(instance == null)
13 | {
14 | synchronized(instance) {
15 | instance = new MoguArrayUtil();
16 | }
17 | }
18 | return instance;
19 | }
20 |
21 |
22 | public int[] arrayUnique(int[] intArray) {
23 | Set intSet = new HashSet();
24 | int intVal;
25 | int length = intArray.length;
26 | for(int i = 0; i < length; i++) {
27 | intVal = intArray[i];
28 | if(!intSet.contains(intVal)) {
29 | intSet.add(intVal);
30 | }
31 | }
32 | Integer[] uniqArray = new Integer[intSet.size()];
33 | intSet.toArray(uniqArray);
34 | int[] ints = new int[uniqArray.length];
35 | for(int i = 0; i < uniqArray.length; i++) {
36 | ints[i] = uniqArray[i];
37 | }
38 | return ints;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/GroupCounterItem.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | public class GroupCounterItem {
4 |
5 | private int userId;
6 |
7 | private int groupId;
8 |
9 | private int groupTotalCount; // 群总消息数
10 |
11 | private int userUnreadCount; // 用户在该群中的未读消息数
12 |
13 | private int lastMessageId;
14 |
15 | public int getUserId() {
16 | return userId;
17 | }
18 |
19 | public void setUserId(int userId) {
20 | this.userId = userId;
21 | }
22 |
23 | public int getGroupId() {
24 | return groupId;
25 | }
26 |
27 | public void setGroupId(int groupId) {
28 | this.groupId = groupId;
29 | }
30 |
31 | public int getGroupTotalCount() {
32 | return groupTotalCount;
33 | }
34 |
35 | public void setGroupTotalCount(int groupTotalCount) {
36 | this.groupTotalCount = groupTotalCount;
37 | }
38 |
39 | public int getUserUnreadCount() {
40 | return userUnreadCount;
41 | }
42 |
43 | public void setUserUnreadCount(int userUnreadCount) {
44 | this.userUnreadCount = userUnreadCount;
45 | }
46 |
47 | public int getLastMessageId() {
48 | return lastMessageId;
49 | }
50 |
51 | public void setLastMessageId(int lastMessageId) {
52 | this.lastMessageId = lastMessageId;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Relationship.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | /**
4 | *
5 | * @ClassName: Relationship
6 | * @Description: 好友关系
7 | * @author ziye - ziye(at)mogujie.com
8 | * @date 2013-7-20 下午5:50:10
9 | *
10 | */
11 | public class Relationship {
12 |
13 | private int relateId;
14 |
15 | private int userId;
16 |
17 | private int friendUserId;
18 |
19 | private int status;
20 |
21 | private int created;
22 |
23 | private int updated;
24 |
25 | public int getRelateId() {
26 | return relateId;
27 | }
28 |
29 | public void setRelateId(int relateId) {
30 | this.relateId = relateId;
31 | }
32 |
33 | public int getUserId() {
34 | return userId;
35 | }
36 |
37 | public void setUserId(int userId) {
38 | this.userId = userId;
39 | }
40 |
41 | public int getFriendUserId() {
42 | return friendUserId;
43 | }
44 |
45 | public void setFriendUserId(int friendUserId) {
46 | this.friendUserId = friendUserId;
47 | }
48 |
49 | public int getStatus() {
50 | return status;
51 | }
52 |
53 | public void setStatus(int status) {
54 | this.status = status;
55 | }
56 |
57 | public int getCreated() {
58 | return created;
59 | }
60 |
61 | public void setCreated(int created) {
62 | this.created = created;
63 | }
64 |
65 | public int getUpdated() {
66 | return updated;
67 | }
68 |
69 | public void setUpdated(int updated) {
70 | this.updated = updated;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Audio.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | public class Audio {
4 |
5 | private int id;
6 |
7 | private int userId;
8 |
9 | private int toUserId;
10 |
11 | private String path;
12 |
13 | private int fileSize;
14 |
15 | private int costTime;
16 |
17 | private int created;
18 |
19 | private byte[] data;
20 |
21 | public int getId() {
22 | return id;
23 | }
24 |
25 | public void setId(int id) {
26 | this.id = id;
27 | }
28 |
29 | public String getPath() {
30 | return path;
31 | }
32 |
33 | public void setPath(String path) {
34 | this.path = path;
35 | }
36 |
37 | public int getUserId() {
38 | return userId;
39 | }
40 |
41 | public void setUserId(int userId) {
42 | this.userId = userId;
43 | }
44 |
45 | public int getToUserId() {
46 | return toUserId;
47 | }
48 |
49 | public void setToUserId(int toUserId) {
50 | this.toUserId = toUserId;
51 | }
52 |
53 | public int getFileSize() {
54 | return fileSize;
55 | }
56 |
57 | public void setFileSize(int fileSize) {
58 | this.fileSize = fileSize;
59 | }
60 |
61 | public int getCostTime() {
62 | return costTime;
63 | }
64 |
65 | public void setCostTime(int costTime) {
66 | this.costTime = costTime;
67 | }
68 |
69 | public int getCreated() {
70 | return created;
71 | }
72 |
73 | public void setCreated(int created) {
74 | this.created = created;
75 | }
76 |
77 | public byte[] getData() {
78 | return data;
79 | }
80 |
81 | public void setData(byte[] data) {
82 | this.data = data;
83 | }
84 |
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/StatisticsContent.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.sql.SQLException;
4 |
5 | import com.mogujie.ares.extend.BaseAction;
6 | import com.mogujie.ares.lib.logger.Logger;
7 | import com.mogujie.ares.lib.logger.LoggerFactory;
8 | import com.mogujie.ares.model.StatisticsModel;
9 |
10 | public class StatisticsContent extends BaseAction{
11 | @SuppressWarnings("unused")
12 | private static final Logger logger = LoggerFactory.getLogger(StatisticsContent.class);
13 |
14 | //userId
15 | public void saveLog(int soure,int protocol,String ip,int userId,int actionType,String os,String userAgent,String flashVersion,String clientVersion,int version){
16 | /*
17 | * C++ =>Java
18 | * source: web1.
19 | protocol: 2
20 | ip: 3707387924
21 | userId: 1000000
22 | os: Mac ox x
23 | userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
24 | flash: 9.0.1 flashplayer 版本号
25 | client: 3.14 as或客户端息的版本号.
26 | */
27 |
28 | /* logger.info("saveLog soure=" + soure
29 | + ", protocol=" + protocol + ", ip=" + ip
30 | + ", userId=" + userId+", actionType=" + actionType+ ", os=" + os+ ", userAgent=" + userAgent+ ", flashVersion=" + flashVersion+ ", clientVersion=" + clientVersion);
31 | */
32 | try {
33 | StatisticsModel.getInstance().saveLog(soure, protocol, ip,actionType, userId, os, userAgent, flashVersion, clientVersion);
34 | } catch (SQLException e) {
35 | e.printStackTrace();
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/FileManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import org.apache.commons.lang.StringUtils;
4 |
5 | import com.mogujie.ares.configure.BizConstants;
6 | import com.mogujie.ares.lib.logger.Logger;
7 | import com.mogujie.ares.lib.logger.LoggerFactory;
8 | import com.mogujie.ares.lib.net.MoguHttp;
9 |
10 | public class FileManager {
11 |
12 | private static final Logger logger = LoggerFactory.getLogger(FileManager.class);
13 | private static FileManager instance = new FileManager();
14 |
15 | private String audioUploadUrl = BizConstants.URL_FILE_UPLOAD; // 文件上传路径
16 |
17 | private String audioDownloadUrl = BizConstants.URL_FILE_DOWNLOAD; // 文件下载路径
18 |
19 | public static FileManager getInstance()
20 | {
21 | if(instance == null)
22 | {
23 | instance = new FileManager();
24 | }
25 | return instance;
26 | }
27 |
28 | /**
29 | *
30 | * @Description: 返回音频文件
31 | * @param bytes
32 | * @param userId
33 | * @return
34 | */
35 | public String saveAudioBinary(byte[] bytes) {
36 |
37 | if(bytes == null || bytes.length == 0) {
38 | return "";
39 | }
40 |
41 | String fileName = MoguHttp.uploadByteFile(this.audioUploadUrl, bytes);
42 | logger.info("保存语音文件成功:" + fileName);
43 |
44 | return fileName;
45 | }
46 |
47 | /**
48 | *
49 | * @Description: 从磁盘读取一个文件
50 | * @param fileName
51 | * @return
52 | */
53 | public byte[] readAudioBinary(String fileName) {
54 | if(StringUtils.isEmpty(fileName)) {
55 | return null;
56 | }
57 | byte[] bytes = MoguHttp.downloadByteFile(this.audioDownloadUrl + fileName);
58 |
59 | return bytes;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/GroupRelation.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | /**
4 | *
5 | * @Description: 群-用户关系
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2014-1-5 下午4:17:37
8 | *
9 | */
10 | public class GroupRelation {
11 |
12 | private int id;
13 |
14 | private int groupId;
15 |
16 | private int userId;
17 |
18 | private int title;
19 |
20 | private int groupType;
21 |
22 | private int status;
23 |
24 | private int created;
25 |
26 | private int updated;
27 |
28 | public int getId() {
29 | return id;
30 | }
31 |
32 | public void setId(int id) {
33 | this.id = id;
34 | }
35 |
36 | public int getGroupId() {
37 | return groupId;
38 | }
39 |
40 | public void setGroupId(int groupId) {
41 | this.groupId = groupId;
42 | }
43 |
44 | public int getUserId() {
45 | return userId;
46 | }
47 |
48 | public void setUserId(int userId) {
49 | this.userId = userId;
50 | }
51 |
52 | public int getTitle() {
53 | return title;
54 | }
55 |
56 | public void setTitle(int title) {
57 | this.title = title;
58 | }
59 |
60 | public int getGroupType() {
61 | return groupType;
62 | }
63 |
64 | public void setGroupType(int groupType) {
65 | this.groupType = groupType;
66 | }
67 |
68 | public int getStatus() {
69 | return status;
70 | }
71 |
72 | public void setStatus(int status) {
73 | this.status = status;
74 | }
75 |
76 | public int getCreated() {
77 | return created;
78 | }
79 |
80 | public void setCreated(int created) {
81 | this.created = created;
82 | }
83 |
84 | public int getUpdated() {
85 | return updated;
86 | }
87 |
88 | public void setUpdated(int updated) {
89 | this.updated = updated;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/GroupMessage.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | public class GroupMessage {
4 |
5 |
6 | // 消息id
7 | private int id;
8 |
9 | private int userId;
10 |
11 | private User userInfo;
12 |
13 | private int groupId;
14 |
15 | private Group groupInfo;
16 |
17 | private String content;
18 |
19 | private int status;
20 |
21 | private int updated;
22 |
23 | private int created;
24 |
25 | public int getId() {
26 | return id;
27 | }
28 |
29 | public void setId(int id) {
30 | this.id = id;
31 | }
32 |
33 | public int getUserId() {
34 | return userId;
35 | }
36 |
37 | public void setUserId(int userId) {
38 | this.userId = userId;
39 | }
40 |
41 | public User getUserInfo() {
42 | return userInfo;
43 | }
44 |
45 | public void setUserInfo(User userInfo) {
46 | this.userInfo = userInfo;
47 | }
48 |
49 | public int getGroupId() {
50 | return groupId;
51 | }
52 |
53 | public void setGroupId(int groupId) {
54 | this.groupId = groupId;
55 | }
56 |
57 | public Group getGroupInfo() {
58 | return groupInfo;
59 | }
60 |
61 | public void setGroupInfo(Group groupInfo) {
62 | this.groupInfo = groupInfo;
63 | }
64 |
65 | public String getContent() {
66 | return content;
67 | }
68 |
69 | public void setContent(String content) {
70 | this.content = content;
71 | }
72 |
73 | public int getStatus() {
74 | return status;
75 | }
76 |
77 | public void setStatus(int status) {
78 | this.status = status;
79 | }
80 |
81 | public int getUpdated() {
82 | return updated;
83 | }
84 |
85 | public void setUpdated(int updated) {
86 | this.updated = updated;
87 | }
88 |
89 | public int getCreated() {
90 | return created;
91 | }
92 |
93 | public void setCreated(int created) {
94 | this.created = created;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/FileManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import org.apache.commons.lang.StringUtils;
4 |
5 | import com.mogujie.ares.configure.BizConstants;
6 | import com.mogujie.ares.lib.logger.Logger;
7 | import com.mogujie.ares.lib.logger.LoggerFactory;
8 | import com.mogujie.ares.lib.net.MoguHttp;
9 |
10 | public class FileManager {
11 |
12 | private static final Logger logger = LoggerFactory.getLogger(FileManager.class);
13 | private static FileManager instance = new FileManager();
14 |
15 | private String audioUploadUrl = BizConstants.URL_FILE_UPLOAD; // 文件上传路径
16 |
17 | private String audioDownloadUrl = BizConstants.URL_FILE_DOWNLOAD; // 文件下载路径
18 |
19 | public static FileManager getInstance()
20 | {
21 | if(instance == null)
22 | {
23 | instance = new FileManager();
24 | }
25 | return instance;
26 | }
27 |
28 | /**
29 | *
30 | * @Description: 返回音频文件
31 | * @param bytes
32 | * @param userId
33 | * @return
34 | */
35 | public String saveAudioBinary(byte[] bytes) {
36 |
37 | if(bytes == null || bytes.length == 0) {
38 | return "";
39 | }
40 |
41 | String fileName = MoguHttp.uploadAudioByteFile(this.audioUploadUrl, bytes);
42 | if(StringUtils.isEmpty(fileName)) {
43 | logger.info("保存语音文件失败");
44 | } else {
45 | logger.info("保存语音文件成功:" + fileName);
46 | }
47 | return fileName;
48 | }
49 |
50 | /**
51 | *
52 | * @Description: 从磁盘读取一个文件
53 | * @param fileName
54 | * @return
55 | */
56 | public byte[] readAudioBinary(String fileName) {
57 | if(StringUtils.isEmpty(fileName)) {
58 | return null;
59 | }
60 | String strDownloadUrl = this.audioDownloadUrl + fileName;
61 | logger.info("下载文件:" + strDownloadUrl);
62 | byte[] bytes = MoguHttp.downloadByteFile(strDownloadUrl);
63 |
64 | return bytes;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ###项目背景
2 | 随着蘑菇街由导购向电商转型,蘑菇街自己的IM也应运而生,IM起初只是用于商家和
3 | 买家之间沟通的工具。后面我们问自己,既然已经有了用于客服的IM,为什么不自己
4 | 做一个IM,用于公司内部的沟通工具,来替换RTX呢,然后就有了TT(TeamTalk)
5 | 的雏形,现在蘑菇街内部的IM工具都是TT来完成的。随着TT的逐渐完善,我们再次
6 | 决定把TT开源,来回馈开源社区,我们希望国内的中小企业都能用上免费开源的
7 | IM内部沟通工具。
8 |
9 | ###系统环境
10 | 服务端平台: Linux
11 | 客户端平台: Windows,Mac, iOS, Android
12 |
13 | ###子系统分类
14 | 各个子系统的详细说明请参考子系统的README文档
15 |
16 | -TTPhpServer
17 | TT的Web后台管理服务器
18 |
19 | -TTCppServer
20 | TT的服务器,包括登陆分配,长连接接入,消息路由,文件传输,
21 | 文件存储等功能的支持
22 |
23 | -TTJavaServer
24 | TT的服务器,只要是作为TT服务器操作MySQL和Redis的代理服务器
25 |
26 | -TTWinClient
27 | Window客户端
28 |
29 | -TTMacClient
30 | Mac系统客户端
31 |
32 | -TTIOSClient
33 | iOS客户端
34 |
35 | -TTAndroidClient
36 | Android客户端
37 |
38 | ###编译安装
39 | 详见各自子系统的INSTALL文件
40 |
41 | ###开发流程
42 |
43 | 开发者流程
44 |
45 | - 开发者是指现在TeamTalk的开发人员,或者以后我们信任的贡献者转化而成的开发人员。
46 |
47 | - 要成为开发者,需要在github上注册账号, 然后由管理者加入到相应项目的collaborators列表
48 |
49 | - 开发主要用到master和develop两个分支, 平时开发都在develop分支上,只有代码
50 | 达到一个milestone的stable状态,才把develop分支merge到master分支
51 |
52 | - 有时开发者可能想实现一个比较cool的feature,可以建立一个feature_x分支,
53 | 测试稳定后merge到master
54 |
55 | 贡献者流程
56 |
57 | - 贡献者是指非TeamTalk项目组成员,热爱开源且希望为开源项目贡献代码的开发人员
58 |
59 | - 贡献者可以在github上Fork一个子项目,然后在Fork的项目上提交代码,
60 | 再通过Pull Request把修改通知给项目开发者,由开发者code review后,
61 | 决定是否merge进入master分支, 具体可参考: [github协作流程](http://www.worldhello.net/gotgithub/04-work-with-others/010-fork-and-pull.html)
62 |
63 | ###版本迭代流程
64 | - 版本迭代周期暂定为3个月
65 | - 开发者和贡献者可以把想要实现的feature通过github的wiki功能提交上来
66 | - 开始迭代前讨论本期版本要实现哪些feature,然后把要在本次迭代实现的featue列表写入版本的TODO feature list列表
67 | - 制定大概的排期
68 | - 开发,内部测试
69 | - alpha版本发布,公测
70 | - 把develop分支代码merge到master分支,stable版本发布
71 |
72 | ###开源协议
73 | [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
74 |
75 | ###Remark
76 | 
77 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/Packet.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | /**
4 | *
5 | * @Description: 发送和接收的数据包
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2013-7-21 下午4:14:31
8 | *
9 | */
10 | public class Packet {
11 |
12 | private int length; // 数据包长度,包括包头
13 |
14 | private int version; // 版本号
15 |
16 | private int flag; // 标记: 分拆数据包,压缩
17 |
18 | private int serviceId; // 服务号, 后端这边固定1000
19 |
20 | private int commandId; // 命令号, 标识服务接口,
21 |
22 | private int error; // 服务器错误
23 |
24 | private int reserved; // 保留,可用于如序列号等
25 |
26 | private DataBuffer contentBuffer; // 业务数据部分
27 |
28 | public int getLength() {
29 | return length;
30 | }
31 |
32 | public void setLength(int length) {
33 | this.length = length;
34 | }
35 |
36 | public int getVersion() {
37 | return version;
38 | }
39 |
40 | public void setVersion(int version) {
41 | this.version = version;
42 | }
43 |
44 | public int getFlag() {
45 | return flag;
46 | }
47 |
48 | public void setFlag(int flag) {
49 | this.flag = flag;
50 | }
51 |
52 | public int getServiceId() {
53 | return serviceId;
54 | }
55 |
56 | public void setServiceId(int serviceId) {
57 | this.serviceId = serviceId;
58 | }
59 |
60 | public int getCommandId() {
61 | return commandId;
62 | }
63 |
64 | public void setCommandId(int commandId) {
65 | this.commandId = commandId;
66 | }
67 |
68 | public int getError() {
69 | return error;
70 | }
71 |
72 | public void setError(int error) {
73 | this.error = error;
74 | }
75 |
76 | public int getReserved() {
77 | return reserved;
78 | }
79 |
80 | public void setReserved(int reserved) {
81 | this.reserved = reserved;
82 | }
83 |
84 | public DataBuffer getContentBuffer() {
85 | return contentBuffer;
86 | }
87 |
88 | public void setContentBuffer(DataBuffer contentBuffer) {
89 | this.contentBuffer = contentBuffer;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/logger/Logger.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.logger;
2 | // ziye copy from mdl-common
3 | public class Logger {
4 |
5 | private final org.slf4j.Logger logger;
6 |
7 | public Logger(Class> key) {
8 | this.logger = org.slf4j.LoggerFactory.getLogger(key);
9 | }
10 |
11 | public Logger(String key) {
12 | this.logger = org.slf4j.LoggerFactory.getLogger(key);
13 | }
14 |
15 | private String appendContextMessage(String msg) {
16 | return "" + msg;
17 | }
18 |
19 | public void debug(String msg) {
20 | try {
21 | logger.debug(appendContextMessage(msg));
22 | } catch (Throwable t) {
23 | }
24 | }
25 |
26 | public void debug(String msg, Throwable e) {
27 | try {
28 | logger.debug(appendContextMessage(msg), e);
29 | } catch (Throwable t) {
30 | }
31 | }
32 |
33 | public void info(String format, Object[] argArray){
34 | try {
35 | logger.info(format,argArray);
36 | } catch (Throwable t) {
37 | }
38 | }
39 |
40 | public void info(String msg) {
41 | try {
42 | logger.info(appendContextMessage(msg));
43 | } catch (Throwable t) {
44 | }
45 | }
46 |
47 | public void info(String msg, Throwable e) {
48 | try {
49 | logger.info(appendContextMessage(msg), e);
50 | } catch (Throwable t) {
51 | }
52 | }
53 |
54 | public void warn(String msg, Throwable e) {
55 | try {
56 | logger.warn(appendContextMessage(msg), e);
57 | } catch (Throwable t) {
58 | }
59 | }
60 |
61 | public void warn(String msg) {
62 | try {
63 | logger.warn(appendContextMessage(msg));
64 | } catch (Throwable t) {
65 | }
66 | }
67 |
68 | public void error(String msg) {
69 | try {
70 | logger.error(appendContextMessage(msg));
71 | } catch (Throwable t) {
72 | }
73 | }
74 |
75 | public void error(String msg, Throwable e) {
76 | try {
77 | logger.error(appendContextMessage(msg), e);
78 | } catch (Throwable t) {
79 | }
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/TransmitFile.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | /**
4 | *
5 | * @Description: 群信息
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2014-1-5 下午4:17:55
8 | *
9 | */
10 | public class TransmitFile {
11 |
12 | private int id;
13 |
14 | private int fromUserId; // 发送人
15 |
16 | private int toUserId; // 接收人
17 |
18 | private String taskId = ""; //任务编号
19 |
20 | private String filePath = ""; // 文件路径
21 |
22 | private int fileSize; //文件大小.
23 |
24 | private int status; // 状态,是否已经被接收
25 |
26 | private int created; // 创建时间
27 |
28 | private int updated; // 更新时间
29 |
30 | public int getId() {
31 | return id;
32 | }
33 |
34 | public void setId(int id) {
35 | this.id = id;
36 | }
37 |
38 | public int getFromUserId() {
39 | return fromUserId;
40 | }
41 |
42 | public void setFromUserId(int fromUserId) {
43 | this.fromUserId = fromUserId;
44 | }
45 |
46 | public int getToUserId() {
47 | return toUserId;
48 | }
49 |
50 | public void setToUserId(int toUserId) {
51 | this.toUserId = toUserId;
52 | }
53 |
54 | public String getFilePath() {
55 | return filePath;
56 | }
57 |
58 | public void setFilePath(String filePath) {
59 | this.filePath = filePath;
60 | }
61 |
62 | public int getCreated() {
63 | return created;
64 | }
65 |
66 | public void setCreated(int created) {
67 | this.created = created;
68 | }
69 |
70 | public int getUpdated() {
71 | return updated;
72 | }
73 |
74 | public void setUpdated(int updated) {
75 | this.updated = updated;
76 | }
77 |
78 | public int getStatus() {
79 | return status;
80 | }
81 |
82 | public void setStatus(int status) {
83 | this.status = status;
84 | }
85 |
86 | public int getFileSize() {
87 | return fileSize;
88 | }
89 |
90 | public void setFileSize(int fileSize) {
91 | this.fileSize = fileSize;
92 | }
93 |
94 | public String getTaskId() {
95 | return taskId;
96 | }
97 |
98 | public void setTaskId(String taskId) {
99 | this.taskId = taskId;
100 | }
101 |
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/configure/SysConstants.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.configure;
2 |
3 |
4 | public class SysConstants {
5 |
6 | // 服务器的默认端口,可以通过启动时的args来修改
7 | public static final int SERVER_DEFAULT_PORT = 11000;
8 |
9 | // 默认消息头的长度
10 | public static final int PROTOCOL_HEADER_LENGTH = 16;
11 |
12 | //packet的当前版本号,最新版本
13 | public static final int PROTOCOL_CURRENT_VERSION = 2;
14 |
15 | public static final int PROTOCOL_PREVIOUS_VERSION=1; //上一版本的版本号
16 |
17 | // 定时任务启动状态
18 | public static final String TASK_ENABLE_RUN = "1";
19 |
20 | // 定时任务停止状态
21 | public static final String TASK_DISENABLE_RUN = "0";
22 |
23 | //停止应用的检查时间
24 | public static final long CHECK_STOP_GAP_TIME = 2000;
25 |
26 | //停止实例配置文件
27 | public static final String ELEGANT_STOP_FILE = "/tmp/.mogutalk_stop";
28 |
29 | public static final String ELEGANT_SHUTDOWN_SHELL_SCRIPT = "mogutalkshutdown";
30 |
31 | //停止实例值
32 | public static final String ELEGANT_STOP_CONTENT = "stop";
33 |
34 | // --------------- redis pool ---------------
35 |
36 | // 从数据库连接池中取得连接时,对其的有效性进行检查
37 | public static final boolean TEST_ON_BORROW = true;
38 |
39 | // 最大连接数
40 | public static final int MAX_ACTIVE = 36;
41 |
42 | // 最大闲置的连接数
43 | public static final int MAX_IDLE = 20;
44 |
45 | // 最小.....
46 | public static final int MIN_IDLE = 5;
47 |
48 | // 请求最长等待时间/毫秒
49 | public static final int MAX_WAIT = 1000;
50 |
51 | // 闲置时测试
52 | public static final boolean TEST_WHILE_IDLE = true;
53 |
54 | // redis db的名字
55 | public static final String REDIS_INSTANCE_NAME_COUNTER = "counter"; // 总消息计数器
56 |
57 | public static final String REDIS_INSTANCE_NAME_SESSION = "session"; // mogujie session
58 |
59 | public static final String REDIS_INSTANCE_NAME_UNREAD = "unread"; // 未读消息计数
60 |
61 | public static final String REDIS_CINFO_PREFIX_KEY="user_c_"; //redis cinfo的前缀key.
62 |
63 | public static final String REDIS_CINFO_SUB_KEY="chatNew"; //redis cinfo 子key
64 |
65 | // --------------- db pool -----------------
66 |
67 | public static final int DB_MIN_CONNECTIONS_PER_PARTITION = 10;
68 |
69 | public static final int DB_MAX_CONNECTIONS_PER_PARTITION = 18;
70 |
71 | public static final int DB_IDLE_CONNECTION_TEST_PERIOD = 60;
72 |
73 | public static final int DB_PARTITION_COUNT = 1;
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Department.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | public class Department {
4 |
5 | protected int departId; // 部门id
6 |
7 | // protected String departName; // 部门名
8 |
9 | protected String title; // 部门标题
10 |
11 | protected String description; // 部门描述
12 |
13 | protected int parentDepartId; // 父部门id(上级部门id)
14 |
15 | protected int leader; // 部门leader
16 |
17 | protected int status; // 部门状态 0:正常 1:删除
18 |
19 | private int created; // 创建时间
20 |
21 | private int updated; // 更新时间
22 |
23 | public int getDepartId() {
24 | return departId;
25 | }
26 |
27 | public void setDepartId(int departId) {
28 | this.departId = departId;
29 | }
30 |
31 | // public String getDepartName() {
32 | // return departName;
33 | // }
34 | //
35 | // public void setDepartName(String departName) {
36 | // this.departName = departName;
37 | // }
38 |
39 | public String getTitle() {
40 | return title;
41 | }
42 |
43 | public void setTitle(String title) {
44 | this.title = title;
45 | }
46 |
47 | public String getDescription() {
48 | return description;
49 | }
50 |
51 | public void setDescription(String description) {
52 | this.description = description;
53 | }
54 |
55 | public int getParentDepartId() {
56 | return parentDepartId;
57 | }
58 |
59 | public void setParentDepartId(int parentDepartId) {
60 | this.parentDepartId = parentDepartId;
61 | }
62 |
63 | public int getLeader() {
64 | return leader;
65 | }
66 |
67 | public void setLeader(int leader) {
68 | this.leader = leader;
69 | }
70 |
71 | public int getStatus() {
72 | return status;
73 | }
74 |
75 | public void setStatus(int status) {
76 | this.status = status;
77 | }
78 |
79 | public int getCreated() {
80 | return created;
81 | }
82 |
83 | public void setCreated(int created) {
84 | this.created = created;
85 | }
86 |
87 | public int getUpdated() {
88 | return updated;
89 | }
90 |
91 | public void setUpdated(int updated) {
92 | this.updated = updated;
93 | }
94 |
95 | public Department() {
96 | // TODO Auto-generated constructor stub
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/ConfigureManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import java.util.HashMap;
4 | import java.util.Properties;
5 | import java.util.concurrent.ConcurrentHashMap;
6 |
7 | import com.mogujie.ares.configure.Configure;
8 | import com.mogujie.ares.configure.Router;
9 | import com.mogujie.ares.lib.logger.Logger;
10 | import com.mogujie.ares.lib.logger.LoggerFactory;
11 | /**
12 | *
13 | * @Description: 配置管理类, 单例
14 | * @author shitou - shitou[at]mogujie.com
15 | * @date 2013-7-22 上午11:15:23
16 | *
17 | */
18 | public class ConfigureManager
19 | {
20 |
21 | public static final Logger logger = LoggerFactory.getLogger(ConfigureManager.class);
22 |
23 | private static ConfigureManager _configureManagerInstance = null;
24 |
25 | public static ConfigureManager getInstance()
26 | {
27 | if(_configureManagerInstance == null)
28 | {
29 | _configureManagerInstance = new ConfigureManager();
30 | }
31 | return _configureManagerInstance;
32 | }
33 |
34 | private Configure configure;
35 |
36 | private ConfigureManager()
37 | {
38 | configure = new Configure();
39 | }
40 |
41 | /**
42 | * 初始化配置,只在启动时调用
43 | */
44 | public void initial() throws Exception
45 | {
46 | reloadAllConfigs();
47 | }
48 |
49 | /**
50 | * db的配置
51 | * @return
52 | */
53 | public Properties getDBConfig()
54 | {
55 | return configure.getDBConfig();
56 | }
57 |
58 | /**
59 | * cache(redis)配置
60 | * @return
61 | */
62 | public Properties getCacheConfig()
63 | {
64 | return configure.getCacheConfig();
65 | }
66 |
67 | /**
68 | * 阿瑞斯系统配置
69 | * @return
70 | */
71 | public Properties getSystemConfig()
72 | {
73 | return configure.getSystemConfig();
74 | }
75 |
76 | public Router getActionRouter() {
77 | return configure.getActionRouter();
78 | }
79 |
80 | /**
81 | * 计划任务脚本配置
82 | * @return
83 | */
84 | public ConcurrentHashMap> getTimerConfig()
85 | {
86 | return configure.getTimerConfig();
87 | }
88 |
89 | /**
90 | * 装载数据
91 | * @throws Exception
92 | */
93 | public void loadAllConfigs() throws Exception
94 | {
95 | configure.loadConfigs();
96 | }
97 |
98 | /**
99 | * 重新装载数据
100 | * @throws Exception
101 | */
102 | public void reloadAllConfigs() throws Exception {
103 | loadAllConfigs();
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/timer/WorkerInfoReloader.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.timer;
2 |
3 | /**
4 | *
5 | * @Description: 配置实时更新的类
6 | * @author shitou - shitou[at]mogujie.com
7 | * @date 2013-7-22 下午2:54:08
8 | *
9 | */
10 | //public class WorkerInfoReloader implements Runnable {
11 | // private Logger logger =
12 | // LoggerFactory.getLogger(WorkerInfoReloader.class);
13 | // @Override
14 | // public void run()
15 | // {
16 | // logger.info("reload worker info");
17 | // //重载所有的配置
18 | // boolean isSuccess = InternalDataModel.getInstance().refreshUserList();
19 | // if(!isSuccess) {
20 | // logger.error("reload worker info list fail");
21 | // }
22 | // logger.info("reload worker info list success");
23 | // }
24 |
25 | // public void recorectData() {
26 | // List groupList = new ArrayList();
27 | // DBManager dbManager = DBManager.getInstance();
28 | // Connection conn = dbManager.getConnection(DBPoolName.macim_slave);
29 | // PreparedStatement statement = null;
30 | // ResultSet rs = null;
31 | // CounterModel cm = CounterModel.getInstance();
32 | // try {
33 | // String sql = "select * from IMGroupRelation where " +
34 | // "status = 1 and groupType = 2 order by updated desc, id desc";
35 | // statement = conn.prepareStatement(sql);
36 | // rs = statement.executeQuery();
37 | // GroupRelation gr = null;
38 | // while(rs.next()) {
39 | // gr = new GroupRelation();
40 | // gr.setGroupId(rs.getInt("groupId"));
41 | // gr.setUserId(rs.getInt("userId"));
42 | // groupList.add(gr);
43 | // }
44 | // int size = groupList.size();
45 | // logger.info("size: " + size);
46 | // for(int i = 0; i < size; i++) {
47 | // gr = groupList.get(i);
48 | // Map grcnt = cm.getUserGroupUnreadCount(gr.getUserId(),
49 | // new int[]{gr.getGroupId()});
50 | // GroupMessage[] gm =
51 | // MessageModel.getInstance().getGroupMessages(gr.getGroupId(), 0, 1);
52 | // logger.info("crt: " + gm[0].getCreated() + ", grcnt: " + grcnt);
53 | // if(gr.getUserId() == 9822376 && grcnt.get(gr.getGroupId()) > 0 &&
54 | // gm[0].getCreated() > 1400752800) {
55 | // // cm.clearUserGroupCounter(gr.getUserId(), gr.getGroupId());
56 | // logger.info("清除消息: userId = " + gr.getUserId()
57 | // + " , groupId = " + gr.getGroupId() + ", cnt=" +
58 | // grcnt.get(gr.getGroupId()));
59 | // }
60 | // }
61 | // } catch (SQLException e) {
62 | // logger.error("", e);
63 | // } finally {
64 | // dbManager.release(DBPoolName.macim_slave, conn, statement, rs);
65 | // }
66 | // }
67 | // }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/storage/CachePool.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.storage;
2 |
3 | import com.mogujie.ares.configure.SysConstants;
4 |
5 | import redis.clients.jedis.Jedis;
6 | import redis.clients.jedis.JedisPool;
7 | import redis.clients.jedis.JedisPoolConfig;
8 |
9 | /**
10 | *
11 | * @Description: 缓存的连接池
12 | * @author shitou - shitou[at]mogujie.com
13 | * @date 2013-7-21 下午4:15:34
14 | *
15 | */
16 | public class CachePool {
17 |
18 | private JedisPool pool = null;
19 |
20 | private JedisPoolConfig poolConfig = null;
21 |
22 | private String host; // 缓存的服务器
23 | private int port; // 端口
24 | private int db; // 连接到第几个db
25 |
26 | public CachePool(String host, Integer port, Integer db)
27 | {
28 | this.host = host;
29 | this.port = port;
30 | this.db = db;
31 | }
32 |
33 | /**
34 | * @Description: 初始化配置
35 | */
36 | public void initialConfig()
37 | {
38 | if(poolConfig == null)
39 | {
40 | poolConfig = new JedisPoolConfig();
41 | poolConfig.setTestOnBorrow(SysConstants.TEST_ON_BORROW); // 从数据库连接池中取得连接时,对其的有效性进行检查
42 | poolConfig.setMaxActive(SysConstants.MAX_ACTIVE); // 最大连接数
43 | poolConfig.setMaxIdle(SysConstants.MAX_IDLE); // 最大闲置的连接数
44 | poolConfig.setMinIdle(SysConstants.MIN_IDLE); // 最少
45 | poolConfig.setMaxWait(SysConstants.MAX_WAIT); // 请求最长等待时间/毫秒
46 | poolConfig.setTestWhileIdle(SysConstants.TEST_WHILE_IDLE); // 闲置时测试
47 | }
48 | }
49 |
50 | /**
51 | *
52 | * @Description: 初始化
53 | */
54 | public void launch()
55 | {
56 | if(pool == null)
57 | {
58 | initialConfig();
59 | pool = new JedisPool(poolConfig, this.host, this.port);
60 | }
61 | }
62 |
63 | /**
64 | *
65 | * @Description: 销毁
66 | */
67 | public void destory()
68 | {
69 | if(pool != null)
70 | {
71 | pool.destroy();
72 | }
73 | }
74 |
75 | /**
76 | *
77 | * @Description: 获得连接
78 | * @return redis的连接
79 | */
80 | public Jedis getResource()
81 | {
82 | Jedis jedisInstance = null;
83 | if(pool != null)
84 | {
85 | jedisInstance = pool.getResource();
86 | if(db > 0)
87 | {
88 | jedisInstance.select(db);
89 | }
90 | }
91 | return jedisInstance;
92 | }
93 |
94 | /**
95 | *
96 | * @Description: 释放连接
97 | * @param jedisInstance
98 | */
99 | public void returnResource(Jedis jedisInstance)
100 | {
101 | pool.returnResource(jedisInstance);
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/dispatch/DefaultRequestDispatcher.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.dispatch;
2 |
3 | import org.jboss.netty.channel.ChannelHandlerContext;
4 | import org.jboss.netty.channel.MessageEvent;
5 |
6 | import com.mogujie.ares.extend.ActionContext;
7 | import com.mogujie.ares.extend.ActionHolder;
8 | import com.mogujie.ares.lib.logger.Logger;
9 | import com.mogujie.ares.lib.logger.LoggerFactory;
10 | import com.mogujie.ares.lib.net.DataBuffer;
11 | import com.mogujie.ares.lib.net.IDispatcher;
12 | import com.mogujie.ares.lib.net.Packet;
13 |
14 | /**
15 | *
16 | * @Description: 默认的消息分发器,所有从BinaryMessageHandler传来的消息
17 | * 由这个类根据消息头的type分发到相应的Action
18 | * @author ziye - ziye[at]mogujie.com
19 | * @date 2013-7-21 上午11:21:34
20 | *
21 | */
22 | public class DefaultRequestDispatcher implements IDispatcher {
23 |
24 | private static final Logger logger = LoggerFactory
25 | .getLogger(DefaultRequestDispatcher.class);
26 |
27 | private ActionHolder actionHolder;
28 |
29 | public DefaultRequestDispatcher(ActionHolder actionHolder) {
30 | this.actionHolder = actionHolder;
31 | }
32 |
33 | public ActionHolder getActionHolder() {
34 | return actionHolder;
35 | }
36 |
37 | public void setActionHolder(ActionHolder actionHolder) {
38 | this.actionHolder = actionHolder;
39 | }
40 |
41 | /**
42 | *
43 | * @Description: 消息分发函数,所有请求都由这个函数来分发
44 | * @param @param requestType
45 | * @param @return
46 | * @return ActionContext
47 | * @throws
48 | */
49 | @Override
50 | public void dispatch(ChannelHandlerContext context, MessageEvent e) {
51 |
52 | Packet packet = (Packet) e.getMessage();
53 | int type = packet.getCommandId();
54 |
55 | ActionContext actionContext = actionHolder.get(type); // 取得请求对应的Action
56 | if (actionContext == null) { // 这里是有可能会返回null的
57 | logger.error("找不到指定的Action, type: " + type);
58 | return;
59 | }
60 |
61 | try {
62 | Object res = actionContext.invoke(context, packet);
63 | if (res != null) {
64 | actionContext.sendResponse(context, packet, (DataBuffer) res); // 发送结果
65 | }
66 | } catch (Exception e1) { // 如果出错了,临死前返回一个数据包给客户端...
67 | logger.error("commandId: " + packet.getCommandId(), e1);
68 | // actionContext.sendResponse(context, packet, new DataBuffer(0));
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/model/LoginModel.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.model;
2 |
3 | import java.sql.Connection;
4 | import java.sql.PreparedStatement;
5 | import java.sql.ResultSet;
6 | import java.sql.SQLException;
7 |
8 | import com.alibaba.druid.util.StringUtils;
9 | import com.mogujie.ares.lib.logger.Logger;
10 | import com.mogujie.ares.lib.logger.LoggerFactory;
11 | import com.mogujie.ares.manager.DBManager;
12 | import com.mogujie.ares.manager.DBManager.DBPoolName;
13 | import com.mogujie.ares.util.MoguUtil;
14 |
15 | /*
16 | * @Description: 登陆相关的所有操作
17 | * @author shuchen - shuchen[at]mogujie.com
18 | * @date 2014-08-04 下午3:20:01
19 | */
20 | public class LoginModel {
21 | private static LoginModel instance = new LoginModel();
22 | private static final Logger logger = LoggerFactory
23 | .getLogger(LoginModel.class);
24 |
25 | public static LoginModel getInstance() {
26 | if (instance == null) {
27 | instance = new LoginModel();
28 | }
29 | return instance;
30 | }
31 |
32 | private LoginModel() {
33 |
34 | }
35 |
36 | /*
37 | *
38 | * @Description: 登陆的验证接口,直接DB拉取已加密密码验证
39 | *
40 | * @param mogujieSession
41 | *
42 | * @return
43 | *
44 | * @throws Exception
45 | */
46 | public boolean auth(String uname, String pwd) throws Exception {
47 | boolean isAuthed = false; // model层不做参数为空判断,会在action层做掉
48 | DBManager dbManager = DBManager.getInstance();
49 | Connection conn = dbManager.getConnection(DBPoolName.macim_slave);
50 | PreparedStatement statement = null;
51 | ResultSet rs = null;
52 | try {
53 | String sql = "select pwd from IMUsers where uname = "
54 | + MoguUtil.getArgsHolder(1);
55 | statement = conn.prepareStatement(sql);
56 | statement.setString(1, uname);
57 | rs = statement.executeQuery();
58 | String passwd = "";
59 | while (rs.next()) {
60 | passwd = rs.getString("pwd");
61 | logger.info("login: " + uname);
62 | if (!StringUtils.isEmpty(passwd) && passwd.equals(pwd)) {
63 | isAuthed = true;
64 | }
65 | }
66 | } catch (SQLException e) {
67 | throw e;
68 | } finally {
69 | dbManager.release(DBPoolName.macim_slave, conn, statement, rs);
70 | }
71 | return isAuthed;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/BinaryMessageHandler.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import org.jboss.netty.channel.ChannelHandlerContext;
4 | import org.jboss.netty.channel.ChannelStateEvent;
5 | import org.jboss.netty.channel.MessageEvent;
6 | import org.jboss.netty.channel.SimpleChannelHandler;
7 |
8 | import com.mogujie.ares.lib.logger.Logger;
9 | import com.mogujie.ares.lib.logger.LoggerFactory;
10 | import com.mogujie.ares.manager.NetworkManager;
11 |
12 | /**
13 | *
14 | * @Description: netty接收数据的handler,所有请求都在这里接收并转发到dispatcher
15 | * @author ziye - ziye[at]mogujie.com
16 | * @date 2013-7-21 下午3:39:34
17 | *
18 | */
19 | public class BinaryMessageHandler extends SimpleChannelHandler {
20 |
21 | private static final Logger logger = LoggerFactory.getLogger(BinaryMessageHandler.class);
22 |
23 | private IDispatcher dispatcher;
24 |
25 | public BinaryMessageHandler(IDispatcher dispatcher) {
26 | this.dispatcher = dispatcher;
27 | }
28 |
29 | /**
30 | * 客户端建立连接
31 | * (non-Javadoc)
32 | * @see org.jboss.netty.channel.SimpleChannelHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent)
33 | */
34 | public void channelConnected(ChannelHandlerContext context, ChannelStateEvent e)
35 | throws Exception {
36 | String clientAddr = context.getChannel().getRemoteAddress().toString();
37 | logger.info("[LoggerFilter:Connection-Keep-alive] client " + clientAddr + " Connected");
38 | NetworkManager.getInstance().addClient(context); // 添加到现有客户端的列表里
39 | super.channelConnected(context, e);
40 | }
41 |
42 | /**
43 | * 收到数据的事件
44 | */
45 | @Override
46 | public void messageReceived(ChannelHandlerContext context, MessageEvent e)
47 | throws Exception {
48 | try{
49 | dispatcher.dispatch(context, e);
50 | } catch(Exception exception) {
51 | logger.error("dispatch error. ", exception);
52 | }
53 | }
54 |
55 | /**
56 | * 客户端断开连接
57 | * (non-Javadoc)
58 | * @see org.jboss.netty.channel.SimpleChannelHandler#channelDisconnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent)
59 | */
60 | public void channelDisconnected(ChannelHandlerContext context,
61 | ChannelStateEvent e) throws Exception {
62 | String clientAddr = context.getChannel().getRemoteAddress().toString();
63 | logger.info("[LoggerFilter:Connection-Keep-alive] client " + clientAddr + " Disconnected");
64 | NetworkManager.getInstance().removeClient(context); // 从现有客户端列表里移除该连接
65 | super.channelDisconnected(context, e);
66 | }
67 |
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Group.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | *
7 | * @Description: 群信息
8 | * @author ziye - ziye[at]mogujie.com
9 | * @date 2014-1-5 下午4:17:55
10 | *
11 | */
12 | public class Group {
13 |
14 | private int groupId; // 群Id
15 |
16 | private String groupName = ""; // 群名
17 |
18 | private String avatar = ""; // 群头像
19 |
20 | private String adesc = ""; // 群描述
21 |
22 | private int createUserId; // 创建者Id
23 |
24 | private int groupType; // 群类型 1:固定群 2:临时群
25 |
26 | private int status; // 群状态 1:正常 0: 删除
27 |
28 | private int memberCnt; // 群成员个数
29 |
30 | private List userIdList;
31 |
32 | private int updated; // 群信息更新时间
33 |
34 | private int created; // 群创建时间
35 |
36 | public int getGroupId() {
37 | return groupId;
38 | }
39 |
40 | public void setGroupId(int groupId) {
41 | this.groupId = groupId;
42 | }
43 |
44 | public String getGroupName() {
45 | return groupName;
46 | }
47 |
48 | public void setGroupName(String groupName) {
49 | this.groupName = groupName;
50 | }
51 |
52 | public String getAvatar() {
53 | return avatar;
54 | }
55 |
56 | public void setAvatar(String avatar) {
57 | this.avatar = avatar;
58 | }
59 |
60 | public String getAdesc() {
61 | return adesc;
62 | }
63 |
64 | public void setAdesc(String adesc) {
65 | this.adesc = adesc;
66 | }
67 |
68 | public int getCreateUserId() {
69 | return createUserId;
70 | }
71 |
72 | public void setCreateUserId(int createUserId) {
73 | this.createUserId = createUserId;
74 | }
75 |
76 | public int getGroupType() {
77 | return groupType;
78 | }
79 |
80 | public void setGroupType(int groupType) {
81 | this.groupType = groupType;
82 | }
83 |
84 | public int getMemberCnt() {
85 | return memberCnt;
86 | }
87 |
88 | public void setMemberCnt(int memberCnt) {
89 | this.memberCnt = memberCnt;
90 | }
91 |
92 | public int getStatus() {
93 | return status;
94 | }
95 |
96 | public void setStatus(int status) {
97 | this.status = status;
98 | }
99 |
100 | public int getUpdated() {
101 | return updated;
102 | }
103 |
104 | public void setUpdated(int updated) {
105 | this.updated = updated;
106 | }
107 |
108 | public int getCreated() {
109 | return created;
110 | }
111 |
112 | public void setCreated(int created) {
113 | this.created = created;
114 | }
115 |
116 | public List getUserIdList() {
117 | return userIdList;
118 | }
119 |
120 | public void setUserIdList(List userIdList) {
121 | this.userIdList = userIdList;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/model/DepartModel.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.model;
2 |
3 | import java.sql.Connection;
4 | import java.sql.PreparedStatement;
5 | import java.sql.ResultSet;
6 | import java.sql.SQLException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import com.mogujie.ares.data.Department;
11 | import com.mogujie.ares.lib.logger.Logger;
12 | import com.mogujie.ares.lib.logger.LoggerFactory;
13 | import com.mogujie.ares.manager.DBManager;
14 | import com.mogujie.ares.manager.DBManager.DBPoolName;
15 |
16 | /*
17 | * @Description: 部门相关的model
18 | * @author ziye
19 | *
20 | */
21 | public class DepartModel {
22 |
23 | private static DepartModel instance = new DepartModel();
24 | @SuppressWarnings("unused")
25 | private final Logger logger = LoggerFactory.getLogger(DepartModel.class);
26 |
27 | public static DepartModel getInstance() {
28 | if (instance == null) {
29 | instance = new DepartModel();
30 | }
31 | return instance;
32 | }
33 |
34 | /*
35 | * @Description: 获取所有部门信息
36 | *
37 | * @return Map 部门的具体信息Map
38 | *
39 | * @throws SQLException
40 | */
41 | public Map getDepartmentInfo() throws SQLException {
42 | Map departInfos = new HashMap();
43 | DBManager dbManager = DBManager.getInstance();
44 | Connection conn = dbManager.getConnection(DBPoolName.macim_slave);
45 | PreparedStatement statement = null;
46 | ResultSet rs = null;
47 | try {
48 | String sql = "select * from IMDepartment where status = 0";
49 | statement = conn.prepareStatement(sql);
50 | rs = statement.executeQuery();
51 | Department department = null;
52 | int departId = 0;
53 | while (rs.next()) {
54 | department = new Department();
55 | departId = rs.getInt("id");
56 | department.setDepartId(departId);
57 | department.setTitle(rs.getString("title"));
58 | department.setLeader(rs.getInt("leader"));
59 | department.setParentDepartId(rs.getInt("pid"));
60 | department.setStatus(rs.getInt("status"));
61 | department.setDescription(rs.getString("desc"));
62 | departInfos.put(departId, department);
63 | }
64 | } catch (SQLException e) {
65 | throw e;
66 | } finally {
67 | dbManager.release(DBPoolName.macim_slave, conn, statement, rs);
68 | }
69 | return departInfos;
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/Message.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | /**
4 | *
5 | * @ClassName: Message
6 | * @Description: 消息类
7 | * @author ziye - ziye(at)mogujie.com
8 | * @date 2013-7-20 下午5:50:36
9 | *
10 | */
11 | public class Message {
12 |
13 | // 消息id
14 | private int id;
15 |
16 | // 发送和接收的用户之间的关系Id,以relationship表中用户id小的在前的记录Id作为两个人的关系id
17 | private int relateId;
18 |
19 | private int fromUserId;
20 |
21 | private User fromUser;
22 |
23 | private int toUserId;
24 |
25 | private User toUser;
26 |
27 | private int type;
28 |
29 | private String content;
30 |
31 | private Audio audio;
32 |
33 | private int isDeleted;
34 |
35 | private int updated;
36 |
37 | private int created;
38 |
39 | public int getId() {
40 | return id;
41 | }
42 |
43 | public void setId(int id) {
44 | this.id = id;
45 | }
46 |
47 | public int getRelateId() {
48 | return relateId;
49 | }
50 |
51 | public void setRelateId(int relateId) {
52 | this.relateId = relateId;
53 | }
54 |
55 | public int getFromUserId() {
56 | return fromUserId;
57 | }
58 |
59 | public void setFromUserId(int fromUserId) {
60 | this.fromUserId = fromUserId;
61 | }
62 |
63 | public int getToUserId() {
64 | return toUserId;
65 | }
66 |
67 | public void setToUserId(int toUserId) {
68 | this.toUserId = toUserId;
69 | }
70 |
71 | public int getType() {
72 | return type;
73 | }
74 |
75 | public void setType(int type) {
76 | this.type = type;
77 | }
78 |
79 | public String getContent() {
80 | return content;
81 | }
82 |
83 | public void setContent(String content) {
84 | this.content = content;
85 | }
86 |
87 | public int getIsDeleted() {
88 | return isDeleted;
89 | }
90 |
91 | public void setIsDeleted(int isDeleted) {
92 | this.isDeleted = isDeleted;
93 | }
94 |
95 | public int getUpdated() {
96 | return updated;
97 | }
98 |
99 | public void setUpdated(int updated) {
100 | this.updated = updated;
101 | }
102 |
103 | public int getCreated() {
104 | return created;
105 | }
106 |
107 | public void setCreated(int created) {
108 | this.created = created;
109 | }
110 |
111 | public User getFromUser() {
112 | return fromUser;
113 | }
114 |
115 | public void setFromUser(User fromUser) {
116 | this.fromUser = fromUser;
117 | }
118 |
119 | public User getToUser() {
120 | return toUser;
121 | }
122 |
123 | public void setToUser(User toUser) {
124 | this.toUser = toUser;
125 | }
126 |
127 | public Audio getAudio() {
128 | return audio;
129 | }
130 |
131 | public void setAudio(Audio audio) {
132 | this.audio = audio;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/FrameBinaryDecoder.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import org.jboss.netty.buffer.ChannelBuffer;
4 | import org.jboss.netty.buffer.ChannelBuffers;
5 | import org.jboss.netty.channel.Channel;
6 | import org.jboss.netty.channel.ChannelHandlerContext;
7 | import org.jboss.netty.handler.codec.frame.FrameDecoder;
8 |
9 | import com.mogujie.ares.configure.SysConstants;
10 | import com.mogujie.ares.lib.logger.Logger;
11 | import com.mogujie.ares.lib.logger.LoggerFactory;
12 |
13 | /**
14 | *
15 | * @Description: 数据包解析器
16 | * @author ziye - ziye[at]mogujie.com
17 | * @date 2013-7-21 下午3:44:58
18 | *
19 | */
20 | public class FrameBinaryDecoder extends FrameDecoder {
21 |
22 | private static final Logger logger = LoggerFactory
23 | .getLogger(FrameBinaryDecoder.class);
24 |
25 | /**
26 | * 解析数据包,主要负责解析数据包前8个字节统一格式的头部信息,生成Packet对象, 剩余的数据部分的解析在后面具体的action处理
27 | */
28 | @Override
29 | protected Object decode(ChannelHandlerContext ctx, Channel channel,
30 | ChannelBuffer buffer) throws Exception {
31 |
32 | if (buffer.readableBytes() < SysConstants.PROTOCOL_HEADER_LENGTH) {
33 | return null;
34 | }
35 |
36 | buffer.markReaderIndex(); // 标记一下
37 | int length = buffer.readInt(); // 消息长度
38 | char version = buffer.readChar(); // 消息version, 1个字节
39 | char flag = buffer.readChar(); // 标记: 分拆数据包,压缩
40 | char serviceId = buffer.readChar(); // 服务号, 后端这边固定1000
41 | char commandId = buffer.readChar(); // 命令号
42 | char error = buffer.readChar(); // 服务器错误
43 | char reserved = buffer.readChar(); // 保留,可用户如序列号等
44 | int contentLength = length - SysConstants.PROTOCOL_HEADER_LENGTH; // 计算数据包中业务数据部分的长度(去除头部长度16)
45 |
46 | if (buffer.readableBytes() < contentLength) {
47 | logger.info("数据长度:" + contentLength);
48 | buffer.resetReaderIndex(); // 返回到上面标记的位置
49 | return null;
50 | }
51 |
52 | ChannelBuffer cBuffer = ChannelBuffers.buffer(contentLength);
53 | buffer.readBytes(cBuffer, contentLength); // 转移所有业务部分的数据到新的byte
54 |
55 | Packet packet = new Packet();
56 | packet.setLength(contentLength);
57 | packet.setVersion(version);
58 | packet.setFlag(flag);
59 | packet.setServiceId(serviceId);
60 | packet.setCommandId(commandId);
61 | packet.setError(error);
62 | packet.setReserved(reserved);
63 | DataBuffer dataBuffer = new DataBuffer(cBuffer); // 数据部分
64 | packet.setContentBuffer(dataBuffer);
65 |
66 | // logger.info("decode packet serviceId : " + packet.getServiceId()
67 | // + " commandId: " + packet.getCommandId());
68 | return packet;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/MainServer.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares;
2 |
3 | import com.mogujie.ares.configure.SysConstants;
4 | import com.mogujie.ares.extend.action.DelayUpdateMonitor;
5 | import com.mogujie.ares.lib.logger.Logger;
6 | import com.mogujie.ares.lib.logger.LoggerFactory;
7 | import com.mogujie.ares.manager.CacheManager;
8 | import com.mogujie.ares.manager.ConfigureManager;
9 | import com.mogujie.ares.manager.DBManager;
10 | import com.mogujie.ares.manager.ElegantStopManager;
11 | import com.mogujie.ares.manager.NetworkManager;
12 | import com.mogujie.ares.manager.TimerManager;
13 |
14 | /**
15 | * 初始化阿瑞斯项目main类
16 | * @author ziye
17 | *
18 | */
19 | public class MainServer {
20 |
21 | private static final Logger logger = LoggerFactory.getLogger(MainServer.class);
22 |
23 | public MainServer() {
24 |
25 | }
26 |
27 | public void bootup(int port) {
28 | try {
29 | initLog();
30 | initConfigure();
31 | initBase(); // 基础部分的启动,包括各种连接池
32 | initTimers();
33 | initNet(port);
34 | startStopChecker(port);
35 | } catch (Exception e) {
36 | logger.error("", e);
37 | ElegantStopManager.getInstance(port).shutdown();
38 | }
39 | }
40 |
41 | /**
42 | * 日志初始化
43 | */
44 | public void initLog() {
45 | // 这里好像没什么好做的...
46 | }
47 |
48 | /**
49 | * 配置相关的初始化
50 | * @throws Exception
51 | */
52 | public void initConfigure() throws Exception {
53 | ConfigureManager.getInstance().loadAllConfigs();
54 | }
55 |
56 | /**
57 | * 包括lib内的连接池等的初始化
58 | * @throws Exception
59 | */
60 | public void initBase() throws Exception {
61 | DBManager dbManager = DBManager.getInstance();
62 | CacheManager cacheManager = CacheManager.getInstance();
63 | if(dbManager == null || cacheManager == null) {
64 | throw new Exception("初始化db和cache连接池出错!");
65 | }
66 | }
67 |
68 | /**
69 | * 网络初始化
70 | * @throws Exception
71 | */
72 | public void initNet(int port) throws Exception {
73 | NetworkManager networkManager = NetworkManager.getInstance();
74 | networkManager.init(port);
75 | }
76 |
77 | /**
78 | * 定时脚本启动
79 | * @throws Exception
80 | */
81 | public void initTimers() throws Exception {
82 | TimerManager.getInstance().lanuch();
83 | DelayUpdateMonitor.getInstance().start();
84 | }
85 |
86 | /**
87 | * 启动结束Check是否结束程序的线程
88 | */
89 | public void startStopChecker(int port) {
90 | ElegantStopManager.getInstance(port).startCheckShutdownThread();
91 | }
92 |
93 | public static void main( String[] args ) {
94 | int port = SysConstants.SERVER_DEFAULT_PORT;
95 | if(args.length >= 1) {
96 | port = Integer.parseInt(args[0]);
97 | }
98 | MainServer server = new MainServer();
99 | server.bootup(port);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/Login.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import com.alibaba.druid.util.StringUtils;
4 | import com.mogujie.ares.data.User;
5 | import com.mogujie.ares.extend.BaseAction;
6 | import com.mogujie.ares.lib.logger.Logger;
7 | import com.mogujie.ares.lib.logger.LoggerFactory;
8 | import com.mogujie.ares.lib.net.DataBuffer;
9 | import com.mogujie.ares.model.LoginModel;
10 | import com.mogujie.ares.model.UserModel;
11 | import com.mogujie.ares.util.MoguUtil;
12 |
13 | /*
14 | * @Description: 用户登陆相关的请求
15 | * @author ziye - ziye[at]mogujie.com
16 | * @date 2013-7-21 下午1:28:17
17 | */
18 | public class Login extends BaseAction {
19 |
20 | private static final Logger logger = LoggerFactory.getLogger(Login.class);
21 |
22 | /*
23 | * @Description: 用户登陆
24 | *
25 | * @param userId 用户Id
26 | *
27 | * @param token 登陆用户请求蘑菇街主站的http://www.mogujie.com/aresapi/login接口来获得
28 | *
29 | * @return
30 | */
31 | public DataBuffer login(String uname, String pwd, DataBuffer attachment,
32 | int version) {
33 | logger.info("login: " + uname);
34 | int resultCode = 0;
35 | boolean isAuthed = false;
36 | User user = null;
37 | if (StringUtils.isEmpty(uname)) {
38 | resultCode = 1;
39 | }
40 |
41 | UserModel userModel = UserModel.getInstance();
42 | try {
43 | isAuthed = LoginModel.getInstance().auth(uname, pwd);
44 | if (isAuthed) {
45 | user = userModel.getUserInfo(uname);
46 | } else {
47 | logger.info("login failed with error password!");
48 | }
49 | } catch (Exception e) {
50 | logger.error("login failed with reason : ", e);
51 | isAuthed = false;
52 | }
53 | isAuthed = (isAuthed && (user != null)); // 用户要存在
54 | resultCode = (isAuthed ? 0 : 1); // 0: 成功,1: 失败
55 | DataBuffer buffer = new DataBuffer();
56 | buffer.writeString(uname);// 用户名
57 | buffer.writeInt(resultCode);
58 | if (isAuthed) {
59 | buffer.writeInt(user.getUserId()); // 用户ID
60 | buffer.writeString(user.getUnick()); // 用户昵称
61 | buffer.writeString(user.getAvatar()); // 用户头像
62 | buffer.writeString(user.getTitle()); // 用户职称
63 | buffer.writeString(user.getPosition()); // 用户地址
64 | buffer.writeInt(user.getStatus()); // 用户在职等状态
65 | buffer.writeInt(user.getSex()); // 用户性别
66 | buffer.writeInt(user.getDepartId()); // 用户所在部门ID
67 | buffer.writeInt(user.getJobNumber()); // 用户工号
68 | buffer.writeString(user.getTelphone()); // 用户电话
69 | buffer.writeString(user.getMail()); // 用户邮箱
70 | logger.info("login success: " + uname + ", " + resultCode);
71 | } else {
72 | logger.info("login error: " + uname + ", " + resultCode);
73 | }
74 |
75 | return MoguUtil.writeAttachments(buffer, attachment);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | System.out
7 |
8 | %d{HH:mm:ss.SSS} [%p] [%c] - %m%n
9 |
10 |
11 | INFO
12 |
13 |
14 |
15 | true
16 | logs/mogutalk.log
17 |
18 | logs/%d{yyyyMMdd}/mogutalk.log
19 | 60
20 |
21 |
22 | %d{HH:mm:ss.SSS} [%p] [%c] - %m%n
23 |
24 |
25 | INFO
26 |
27 |
28 |
29 | true
30 | logs/mogutalk-error.log
31 |
32 | logs/%d{yyyyMMdd}/mogutalk-error.log
33 | 60
34 |
35 |
36 | %d{HH:mm:ss.SSS} [%p] [%c] - %m%n
37 |
38 |
39 | ERROR
40 |
41 |
42 |
43 | true
44 | logs/mogutalk-nonefiltered.log
45 |
46 | logs/%d{yyyyMMdd}/mogutalk-nonefiltered.log
47 | 60
48 |
49 |
50 | %d{HH:mm:ss.SSS} [%p] [%c] - %m%n
51 |
52 |
53 |
54 |
55 |
56 | true
57 | logs/mogutalk-connection.log
58 |
59 | logs/%d{yyyyMMdd}/mogutalk-connection.log
60 | 60
61 |
62 |
63 | %d{HH:mm:ss.SSS} [%p] [%c] - %m%n
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/model/StatisticsModel.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.model;
2 |
3 | import java.net.InetAddress;
4 | import java.net.UnknownHostException;
5 | import java.sql.Connection;
6 | import java.sql.PreparedStatement;
7 | import java.sql.SQLException;
8 |
9 | import com.mogujie.ares.manager.DBManager;
10 | import com.mogujie.ares.manager.DBManager.DBPoolName;
11 |
12 | /*
13 | * @Description: 统计模块处理.
14 | * @author zuoye@mogujie.com
15 | */
16 | public class StatisticsModel {
17 | private static StatisticsModel instance = new StatisticsModel();
18 |
19 | public static StatisticsModel getInstance() {
20 | if (instance == null) {
21 | instance = new StatisticsModel();
22 | }
23 | return instance;
24 | }
25 |
26 | public void saveLog(int soure, int protocol, String ip, int type,
27 | int userId, String os, String userAgent, String flashVersion,
28 | String clientVersion) throws SQLException {
29 | DBManager dbManager = DBManager.getInstance();
30 | Connection conn = dbManager.getConnection(DBPoolName.macim_master);
31 | PreparedStatement statement = null;
32 | try {
33 | String sql = " insert into IMLogging (source,protocol,ip,type,userId,os,userAgent,flash,client,created) values(?,?,?,?,?,?,?,?,?,?)";
34 | statement = conn.prepareStatement(sql);
35 | int index = 1;
36 | statement.setObject(index++, soure);
37 | statement.setObject(index++, protocol);
38 | statement.setObject(index++, ip2long(ip));
39 | statement.setObject(index++, type);
40 | statement.setObject(index++, userId);
41 | statement.setObject(index++, os);
42 | statement.setObject(index++, userAgent);
43 | statement.setObject(index++, flashVersion);
44 | statement.setObject(index++, clientVersion);
45 | statement.setObject(index++, System.currentTimeMillis() / 1000);
46 | statement.executeUpdate();
47 | } catch (SQLException e) {
48 | throw e;
49 | } finally {
50 | dbManager.release(DBPoolName.macim_master, conn, statement, null);
51 | }
52 | }
53 |
54 | public long ip2long(String ip) {
55 | int ipNum = 0;
56 | try {
57 | ipNum = str2Ip(ip);
58 | } catch (UnknownHostException e) {
59 | e.printStackTrace();
60 | }
61 | return int2long(ipNum);
62 | }
63 |
64 | public long int2long(int i) {
65 | long l = i & 0x7fffffffL;
66 | if (i < 0) {
67 | l |= 0x080000000L;
68 | }
69 | return l;
70 | }
71 |
72 | public int str2Ip(String ip) throws UnknownHostException {
73 | InetAddress address = InetAddress.getByName(ip);// 在给定主机名的情况下确定主机的 IP 址。
74 | byte[] bytes = address.getAddress();// 返回此 InetAddress 对象的原始 IP 地址
75 | int a, b, c, d;
76 | a = byte2int(bytes[0]);
77 | b = byte2int(bytes[1]);
78 | c = byte2int(bytes[2]);
79 | d = byte2int(bytes[3]);
80 | int result = (a << 24) | (b << 16) | (c << 8) | d;
81 | return result;
82 | }
83 |
84 | public int byte2int(byte b) {
85 | int l = b & 0x07f;
86 | if (b < 0) {
87 | l |= 0x80;
88 | }
89 | return l;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/timer/Timer.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.timer;
2 |
3 | import java.util.Properties;
4 | import java.util.concurrent.Executors;
5 | import java.util.concurrent.ScheduledExecutorService;
6 | import java.util.concurrent.ScheduledFuture;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import com.mogujie.ares.manager.ConfigureManager;
10 |
11 | /**
12 | *
13 | * @Description: 定时器,实际上就是一个ScheduledExecutorService的封装类
14 | * @author shitou - shitou[at]mogujie.com
15 | * @date 2013-7-22 下午2:55:21
16 | *
17 | */
18 | public class Timer
19 | {
20 | private Properties systemProperties;
21 |
22 | private ScheduledExecutorService schedulerService; // 线程池
23 |
24 | private boolean isLaunch = false;
25 |
26 | private static Timer timerInstance = new Timer();
27 |
28 | public static Timer getInstance()
29 | {
30 | if(timerInstance == null)
31 | {
32 | timerInstance = new Timer();
33 | }
34 | return timerInstance;
35 | }
36 |
37 | private Timer()
38 | {
39 | systemProperties = ConfigureManager.getInstance().getSystemConfig();
40 | launch();
41 | }
42 |
43 | // 初始化配置
44 | private void launch()
45 | {
46 | if( ! isLaunch && schedulerService == null)
47 | {
48 | schedulerService = Executors.newScheduledThreadPool(
49 | Integer.valueOf(systemProperties.getProperty("task_initialize_pool")));
50 | }
51 | }
52 |
53 | /**
54 | *
55 | * @Description: 启动一个只运行一次的任务
56 | * @param task
57 | * @param delay 等待delay时间之后再执行
58 | * @param unit
59 | * @return
60 | */
61 | public ScheduledFuture> submitOneShotTask(Runnable task, long delay, TimeUnit unit)
62 | {
63 | ScheduledFuture> scheduler = schedulerService.schedule(task, delay, unit);
64 | return scheduler;
65 | }
66 |
67 | /**
68 | *
69 | * @Description: 启动一个固定延迟周期执行的脚本,
70 | * 指上一次执行结束之后延迟period时间之后再执行
71 | * 如initialDelay = 1; delay = 3; unit = second; 执行时间固定需要2秒
72 | * 从time=0开始执行的时间应该是1, 6, 11, 16....
73 | * @param task
74 | * @param initialDelay
75 | * @param period
76 | * @param unit
77 | * @return
78 | */
79 | public ScheduledFuture> submitFixedRateTask(Runnable task, long initialDelay, long period, TimeUnit unit)
80 | {
81 | ScheduledFuture> scheduler = schedulerService.scheduleAtFixedRate(task, initialDelay, period, unit);
82 | return scheduler;
83 | }
84 |
85 | /**
86 | *
87 | * @Description: 启动一个固定周期执行的脚本,不管上一个任务执行多久,
88 | * 下一个任务都是在上一个开始执行的delay时间之后执行
89 | * 如initialDelay = 1; delay = 3; unit = second; 执行时间固定需要2秒
90 | * 从time=0开始执行的时间应该是1, 4, 7, 10....
91 | * @param task
92 | * @param initialDelay
93 | * @param delay
94 | * @param unit
95 | * @return
96 | */
97 | public ScheduledFuture> submitFixedDelayTask(Runnable task, long initialDelay, long delay, TimeUnit unit)
98 | {
99 | ScheduledFuture> scheduler = schedulerService.scheduleWithFixedDelay(task, initialDelay, delay, unit);
100 | return scheduler;
101 | }
102 |
103 | /**
104 | * @Description: 关闭所有任务
105 | */
106 | public void shutDown()
107 | {
108 | if(schedulerService != null)
109 | {
110 | schedulerService.shutdown();
111 | }
112 | }
113 |
114 | /**
115 | * @Description: 关闭所有任务
116 | */
117 | public void shutDownNow()
118 | {
119 | if(schedulerService != null)
120 | {
121 | schedulerService.shutdownNow();
122 | }
123 | }
124 |
125 |
126 | }
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/configure/BizConstants.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.configure;
2 |
3 | /**
4 | *
5 | * @Description: 业务相关的常量定义
6 | * @author ziye - ziye[at]mogujie.com
7 | * @date 2013-9-7 下午1:11:34
8 | *
9 | */
10 | public class BizConstants {
11 |
12 | // 默认的serviceId
13 | public static final int DEFAULT_SERVICEID = 7; // 与C++端通信的ID
14 |
15 | // ---------------- 消息类型的常量定义 ------------------
16 |
17 | public static final int COMMANDID_HEARTBEAT = 1; // 心跳包的commandId
18 |
19 | public static final int COMMANDID_STOP_RECEIVE = 100; // 停止的commandId
20 |
21 | public static final int COMMANDID_SEND_MESSAGE = 111; // 发送消息的commandId
22 |
23 | // ---------------- 用户角色定义 ----------------
24 |
25 | public static final int ROLE_NORMAL_USER = 0; // normal user
26 |
27 | public static final int ROLE_SERVICE_USER = 1; // admin user
28 |
29 | // ---------------- 用户信息默认值 ---------------
30 |
31 | public static final String USER_INFO_DEFAULT_AVATAR = "/avatar/avatar_default.jpg"; // 默认用户头像
32 |
33 | public static final String GROUP_INFO_DEFAULT_AVATAR = "/avatar/group_avatar_default.jpg"; // 默认群组头像
34 |
35 | // URL
36 | public static final String URL_FILE_UPLOAD = "http://1.1.1.1:1/"; // 上传文件
37 |
38 | public static final String URL_FILE_DOWNLOAD = "http://1.1.1.1:1/"; // 下载文件
39 |
40 | // ---------------- 消息 -------------------
41 | // 消息里图片的占位符
42 | public static final String MESSAGE_IMAGE_PREFIX = "image_prefix_&%@##@%*^$%{:{}}";
43 | public static final String MESSAGE_IMAGE_SUFFIX = "image_suffix_*^&%#{:{}}";
44 | public static final String Message_IMAGE_URL_REGEX = "(http://)?s\\d{1,2}";
45 | public static final String MESSAGE_IMAGE_FULL_PLACEHOLDER_REGEX = "(&\\$#@~\\^@\\[\\{:)"
46 | + "((http[s]?://)?s\\d{1,2}\\.ttim\\.(org))(/pic/\\d{6}/[0-9a-z_]{30,40}\\d{1,10}x\\d{1,10}.*"
47 | + ":\\}\\]&\\$~@#@)"; // 图片占位符的正则表达式
48 | public static final String MESSAGE_IMAGE_PART_PLACEHOLDER_REGEX = "(&\\$#@~\\^@\\[\\{:)"
49 | + "(/pic/\\d{6}/[0-9a-z_]{30,40}\\d{1,10}x\\d{1,10}.*"
50 | + ":\\}\\]&\\$~@#@)"; // 图片占位符的正则表达式
51 |
52 | // ------------ IM消息的type ------------
53 | public static final int MESSAGE_TYPE_IM = 1; // 普通用户+系统消息
54 | public static final int MESSAGE_TYPE_IM_AUDIO = 2;
55 | public static final int MESSAGE_TYPE_IM_GROUP = 17;
56 | public static final int MESSAGE_TYPE_IM_GROUP_AUDIO = 18;
57 |
58 | // 群组
59 | public static final String GROUP_TOTAL_MSG_COUNTER_REDIS_KEY_SUFFIX = "_group_msg"; // GROUP_TOTAL_MSG_COUNTER_REDIS_KEY_SUFFIX
60 |
61 | // {userId} + "_" + {groupId} + GROUP_USER_MSG_COUNTER_REDIS_KEY_SUFFIX
62 | public static final String GROUP_USER_MSG_COUNTER_REDIS_KEY_SUFFIX = "_user_group";
63 |
64 | // hashtable里的两个subkey
65 | public static final String GROUP_COUNTER_SUBKEY_COUNTER = "count";
66 | public static final String GROUP_COUNTER_SUBKEY_LASTID = "lastId";
67 |
68 | public static final int GROUP_UNREAD_MAX_COUNTER = 150; // 用户一次读取未读群消息不能超过200
69 |
70 | public static final int GROUP_TYPE_FIXED = 1;
71 |
72 | public static final int GROUP_TYPE_TEMP = 2;
73 |
74 | public static final int UNREAD_MAX_COUNTER = 100; // 用户一次读取未读消息不能超过100
75 |
76 | // ------------- 服务号小T ---------------
77 | public static final int SYS_SERVER_USER_ID = 10000;
78 | public static final String SYS_SERVER_USER_NAME = "小T";
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/Dashboard.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.lang.management.ManagementFactory;
4 |
5 | import com.mogujie.ares.extend.BaseAction;
6 | import com.mogujie.ares.lib.net.DataBuffer;
7 | import com.sun.management.OperatingSystemMXBean;
8 |
9 | @SuppressWarnings("restriction")
10 | public class Dashboard extends BaseAction {
11 |
12 | private PerformanceMonitor cpuMonitor;
13 |
14 | public Dashboard() {
15 | // Create CPU monitoring class
16 | cpuMonitor = new PerformanceMonitor();
17 | cpuMonitor.getCpuUsage(); // Set baseline
18 | }
19 |
20 | public DataBuffer getServerStatus(int version) {
21 |
22 | DataBuffer buffer = new DataBuffer();
23 |
24 | // system info
25 | double cpu = roundToDecimals(cpuMonitor.getCpuUsage() * 100, 2);
26 | long freeMem = Runtime.getRuntime().freeMemory();
27 | long maxMem = Runtime.getRuntime().maxMemory();
28 | long totalMem = Runtime.getRuntime().totalMemory();
29 |
30 | buffer.writeDouble(cpu);
31 | buffer.writeLong(freeMem);
32 | buffer.writeLong(maxMem);
33 | buffer.writeLong(totalMem);
34 |
35 | // threads list
36 | long totalThreadsCpuTime = 0;
37 | long[] threadIds = ManagementFactory.getThreadMXBean()
38 | .getAllThreadIds();
39 |
40 | buffer.writeInt(threadIds.length); // 多少条线程.
41 | for (int i = 0; i < threadIds.length; i++) {
42 | long id = threadIds[i];
43 | String name = ManagementFactory.getThreadMXBean().getThreadInfo(id)
44 | .getThreadName();
45 | long cpuTime = 0;
46 | if (ManagementFactory.getThreadMXBean().isThreadCpuTimeSupported()
47 | && ManagementFactory.getThreadMXBean()
48 | .isThreadCpuTimeEnabled()) {
49 | cpuTime = ManagementFactory.getThreadMXBean().getThreadCpuTime(
50 | id);
51 | totalThreadsCpuTime += cpuTime;
52 | }
53 | buffer.writeLong(id);
54 | buffer.writeString(name);
55 | buffer.writeLong(cpuTime);
56 | }
57 |
58 | buffer.writeLong(totalThreadsCpuTime);
59 |
60 | return buffer;
61 | }
62 |
63 | private static double roundToDecimals(double d, int c) {
64 | int temp = (int) ((d * Math.pow(10, c)));
65 | return (double) (temp / Math.pow(10, c));
66 | }
67 |
68 | private class PerformanceMonitor {
69 | private long lastSystemTime = 0;
70 | private long lastProcessCpuTime = 0;
71 | OperatingSystemMXBean osMxBean = (OperatingSystemMXBean) ManagementFactory
72 | .getOperatingSystemMXBean();
73 |
74 | public synchronized double getCpuUsage() {
75 | if (lastSystemTime == 0) {
76 | baselineCounters();
77 | return 0;
78 | }
79 |
80 | long systemTime = System.nanoTime();
81 | long processCpuTime = osMxBean.getProcessCpuTime();
82 |
83 | double cpuUsage = (double) (processCpuTime - lastProcessCpuTime)
84 | / (systemTime - lastSystemTime);
85 |
86 | lastSystemTime = systemTime;
87 | lastProcessCpuTime = processCpuTime;
88 |
89 | return cpuUsage / osMxBean.getAvailableProcessors();
90 | }
91 |
92 | private void baselineCounters() {
93 | lastSystemTime = System.nanoTime();
94 | lastProcessCpuTime = osMxBean.getProcessCpuTime();
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/ActionContext.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | import org.jboss.netty.channel.ChannelHandlerContext;
6 |
7 | import com.mogujie.ares.extend.filter.IFilter;
8 | import com.mogujie.ares.lib.net.DataBuffer;
9 | import com.mogujie.ares.lib.net.Packet;
10 |
11 |
12 | /**
13 | *
14 | * @Description: Action的描述类
15 | * @author ziye - ziye[at]mogujie.com
16 | * @date 2013-7-22 下午4:45:28
17 | *
18 | */
19 | public class ActionContext {
20 |
21 | private BaseAction action; // Action对象Object
22 |
23 | private Method doMethod; // 处理业务的方法
24 |
25 | private RequestParams requestParams; // 请求参数控制器
26 |
27 | private int requestType; // 请求头的type
28 |
29 | private int defaultResponseType; // 默认响应的type
30 |
31 | private IFilter[] filters; // 过滤器们
32 |
33 | public BaseAction getAction() {
34 | return action;
35 | }
36 |
37 | public void setAction(BaseAction action) {
38 | this.action = action;
39 | }
40 |
41 | public Method getDoMethod() {
42 | return doMethod;
43 | }
44 |
45 | public void setDoMethod(Method doMethod) {
46 | this.doMethod = doMethod;
47 | }
48 |
49 | public RequestParams getRequestParams() {
50 | return requestParams;
51 | }
52 |
53 | public void setRequestParams(RequestParams requestParams) {
54 | this.requestParams = requestParams;
55 | }
56 |
57 | public int getRequestType() {
58 | return requestType;
59 | }
60 |
61 | public void setRequestType(int requestType) {
62 | this.requestType = requestType;
63 | }
64 |
65 | public int getDefaultResponseType() {
66 | return defaultResponseType;
67 | }
68 |
69 | public void setDefaultResponseType(int defaultResponseType) {
70 | this.defaultResponseType = defaultResponseType;
71 | }
72 |
73 | public IFilter[] getFilters() {
74 | return filters;
75 | }
76 |
77 | public void setFilters(IFilter[] filters) {
78 | this.filters = filters;
79 | }
80 |
81 | public Object invoke(ChannelHandlerContext context, Packet packet) throws Exception {
82 | // 解析请求的参数
83 | Object[] params = decode(packet.getContentBuffer(), context, this,packet.getVersion());
84 |
85 |
86 | /*
87 | * 1.确定哪些处理器类是需要处理兼容版本的.
88 | * 2.需要兼容版本的多传一个请求过来的版本号.
89 | * 3.根据版本号.分别作相应的处理.
90 |
91 |
92 | if(action.isCompatibility()){ //需要考虑版本兼容性.
93 | Object[] newParams = new Object[params.length+1];
94 | System.arraycopy(params, 0, newParams, 0, params.length);
95 | newParams[params.length]=packet.getVersion();
96 | }
97 | */
98 |
99 | return doMethod.invoke(action, params); // 调用
100 | }
101 |
102 | /**
103 | *
104 | * @Description: 数据包的解析,一个单纯的代理方法
105 | * @param dataBuffer
106 | * @param context
107 | * @return
108 | * @throws Exception
109 | */
110 | public Object[] decode(DataBuffer dataBuffer, ChannelHandlerContext context, ActionContext actionContext,int version) throws Exception {
111 | return requestParams.decode(dataBuffer, context, actionContext,version);
112 | }
113 |
114 | // 发送消息
115 | public void sendResponse(ChannelHandlerContext context, Packet packet, DataBuffer responseBuffer) {
116 | Packet message = new Packet();
117 | message.setVersion(packet.getVersion());
118 | message.setFlag(packet.getFlag());
119 | message.setServiceId(packet.getServiceId());
120 | message.setCommandId(defaultResponseType);
121 | message.setError(packet.getError());
122 | message.setReserved(packet.getReserved());
123 | if(responseBuffer == null) {
124 | responseBuffer = new DataBuffer(0);
125 | }
126 | message.setContentBuffer(responseBuffer);
127 |
128 | context.getChannel().write(message);
129 | }
130 | }
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/data/User.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.data;
2 |
3 | /**
4 | *
5 | * @ClassName: User
6 | * @Description: 用户对象描述类
7 | * @author ziye - ziye(at)mogujie.com
8 | * @date 2013-7-20 下午5:49:23
9 | *
10 | */
11 | public class User {
12 |
13 | protected int userId; // 用户id
14 |
15 | protected String uname; // 用户名
16 |
17 | protected String unick; // 用户昵称
18 |
19 | protected String avatar; // 用户头像
20 |
21 | protected String title; // 职务
22 |
23 | protected String position; // 地址,为什么不是address?
24 |
25 | protected int status; // 用户在职状态 0:正常(在职) 1:删除(离职) 可扩展
26 |
27 | protected int sex; // 性别
28 |
29 | protected int userType = 0; // 即identity,用户身份标识, 0-普通用户 1-管理员
30 |
31 | private int departId; // 部门id
32 |
33 | private int jobNumber; // 工号
34 |
35 | private String telphone; // 电话
36 |
37 | private String mail; // 邮箱
38 |
39 | private int created; // 创建时间
40 |
41 | private int updated; // 更新时间
42 |
43 | public int getUserId() {
44 | return userId;
45 | }
46 |
47 | public void setUserId(int userId) {
48 | this.userId = userId;
49 | }
50 |
51 | public String getUname() {
52 | return uname;
53 | }
54 |
55 | public void setUname(String uname) {
56 | this.uname = uname;
57 | }
58 |
59 | public String getAvatar() {
60 | return avatar;
61 | }
62 |
63 | public void setAvatar(String avatar) {
64 | this.avatar = avatar;
65 | }
66 |
67 | public int getDepartId() {
68 | return departId;
69 | }
70 |
71 | public void setDepartId(int departId) {
72 | this.departId = departId;
73 | }
74 |
75 | public String getUnick() {
76 | return unick;
77 | }
78 |
79 | public void setUnick(String unick) {
80 | this.unick = unick;
81 | }
82 |
83 | public String getTelphone() {
84 | return telphone;
85 | }
86 |
87 | public void setTelphone(String telphone) {
88 | this.telphone = telphone;
89 | }
90 |
91 | public int getCreated() {
92 | return created;
93 | }
94 |
95 | public void setCreated(int created) {
96 | this.created = created;
97 | }
98 |
99 | public int getUpdated() {
100 | return updated;
101 | }
102 |
103 | public void setUpdated(int updated) {
104 | this.updated = updated;
105 | }
106 |
107 | public int getSex() {
108 | return sex;
109 | }
110 |
111 | public void setSex(int sex) {
112 | this.sex = sex;
113 | }
114 |
115 | public String getTitle() {
116 | return title;
117 | }
118 |
119 | public void setTitle(String title) {
120 | this.title = title;
121 | }
122 |
123 | public String getPosition() {
124 | return position;
125 | }
126 |
127 | public void setPosition(String position) {
128 | this.position = position;
129 | }
130 |
131 | public int getStatus() {
132 | return status;
133 | }
134 |
135 | public void setStatus(int status) {
136 | this.status = status;
137 | }
138 |
139 | public int getUserType() {
140 | return userType;
141 | }
142 |
143 | public void setUserType(int user_type) {
144 | this.userType = user_type;
145 | }
146 |
147 | public int getJobNumber() {
148 | return jobNumber;
149 | }
150 |
151 | public void setJobNumber(int jobNumber) {
152 | this.jobNumber = jobNumber;
153 | }
154 |
155 | /**
156 | * @return the mail
157 | */
158 | public String getMail() {
159 | return mail;
160 | }
161 |
162 | /**
163 | * @param mail
164 | * the mail to set
165 | */
166 | public void setMail(String mail) {
167 | this.mail = mail;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/ActionHolder.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend;
2 |
3 | import java.lang.reflect.Method;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 |
9 | import com.mogujie.ares.configure.Router;
10 | import com.mogujie.ares.configure.Router.ActionDescricptor;
11 | import com.mogujie.ares.extend.filter.IFilter;
12 |
13 | /**
14 | *
15 | * @Description:
16 | * action的集合,在系统初始化的时候会把所有的action都初始化一遍,
17 | * 缓存在这里,每个Action用一个ActionItem描述
18 | * ps: 这个类只在系统初始化的时候有修改,而且是单线程的,
19 | * 后面多线程的都是读操作,不用做并发控制
20 | * @author ziye - ziye[at]mogujie.com
21 | * @date 2013-7-22 下午4:22:55
22 | *
23 | */
24 | public class ActionHolder {
25 |
26 | /**
27 | * key: 请求头上的type
28 | * value: 具体action的描述ActionItem
29 | */
30 | private Map actionMap = new HashMap();
31 |
32 | private Map actionObjectMap = new HashMap();
33 |
34 | public ActionHolder() {
35 | }
36 |
37 | /**
38 | *
39 | * @Description: 根据请求的type获得相应的ActionItem
40 | * @param type
41 | * @return
42 | */
43 | public ActionContext get(int type) {
44 | return actionMap.get(type);
45 | }
46 |
47 | /**
48 | *
49 | * @Description: 把一个Router里的所有的Action加入到Action中
50 | * @param router
51 | * @throws Exception
52 | */
53 | public void put(Router router) throws Exception {
54 | if(router == null) { return ; }
55 | Map actionDescMap = router.getActionMap();
56 | if(actionDescMap == null || actionDescMap.isEmpty()) { return ; }
57 |
58 | Iterator> it = actionDescMap.entrySet().iterator();
59 | ActionDescricptor actionDesc = null;
60 | while(it.hasNext()) {
61 | Entry entry = it.next();
62 | actionDesc = entry.getValue();
63 | put(actionDesc);
64 | }
65 | }
66 |
67 | /**
68 | *
69 | * @Description: 新增一个Action
70 | * @param actionDesc
71 | * @throws Exception
72 | */
73 | @SuppressWarnings({"rawtypes"})
74 | public void put(ActionDescricptor actionDesc) throws Exception {
75 |
76 | if(actionDesc == null) {
77 | return ;
78 | }
79 |
80 | try {
81 | ActionContext item = new ActionContext();
82 |
83 | // action类
84 | String className = actionDesc.getActionClass();
85 | BaseAction actionObject = actionObjectMap.get(className);
86 | if(actionObject == null) {
87 | Class actionClazz = Class.forName(className);
88 | actionObject = (BaseAction)actionClazz.newInstance();
89 | actionObjectMap.put(className, actionObject);
90 | }
91 | item.setAction(actionObject);
92 |
93 | // 请求参数
94 | RequestParams requestParams = new RequestParams();
95 | Map paramsdefine = actionDesc.getParams();
96 | Class[] paramTypes = requestParams.addParams(paramsdefine);
97 | item.setRequestParams(requestParams);
98 |
99 | // 处理业务逻辑的方法
100 | Method method = actionObject.getClass().getMethod(actionDesc.getMethod(), paramTypes);
101 | item.setDoMethod(method);
102 |
103 | // 请求类型
104 | item.setRequestType(actionDesc.getRequestType());
105 | item.setDefaultResponseType(actionDesc.getResponseType());
106 |
107 | // 过滤器,暂时还没有
108 | String[] strFilters = actionDesc.getFilters();
109 | String fname;
110 | IFilter filter;
111 | Class filterClazz;
112 | IFilter[] filters = new IFilter[strFilters.length];
113 | for(int i = 0; i < strFilters.length; i++) {
114 | fname = strFilters[i];
115 | filterClazz = Class.forName(fname);
116 | filter = (IFilter)filterClazz.newInstance();
117 | filters[i] = filter;
118 | }
119 | item.setFilters(filters);
120 |
121 | actionMap.put(item.getRequestType(), item);
122 | } catch (Exception e) {
123 | throw e;
124 | }
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/FrameBinaryEncoder.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | import org.jboss.netty.buffer.ChannelBuffer;
6 | import org.jboss.netty.buffer.ChannelBuffers;
7 | import org.jboss.netty.channel.ChannelHandlerContext;
8 | import org.jboss.netty.channel.Channels;
9 | import org.jboss.netty.channel.MessageEvent;
10 | import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
11 |
12 | import com.mogujie.ares.configure.SysConstants;
13 | import com.mogujie.ares.lib.logger.Logger;
14 | import com.mogujie.ares.lib.logger.LoggerFactory;
15 |
16 | /**
17 | *
18 | * @Description: 数据编码器,把要发送的数据按照一定格式拼接成二进制的流
19 | * @author ziye - ziye[at]mogujie.com
20 | * @date 2013-7-21 下午4:02:07
21 | *
22 | */
23 | public class FrameBinaryEncoder extends SimpleChannelDownstreamHandler {
24 | private static final Logger logger = LoggerFactory
25 | .getLogger(FrameBinaryEncoder.class);
26 |
27 | /**
28 | * @Description: 把要发送的数据编码成二进制数据并发送
29 | */
30 | public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
31 | throws Exception {
32 |
33 | Packet request = (Packet) e.getMessage();
34 | // encode
35 | DataBuffer contentBuffer = request.getContentBuffer();
36 | ChannelBuffer dataBuffer = null;
37 | if (contentBuffer != null) {
38 | dataBuffer = contentBuffer.getOrignalBuffer();
39 | } // 数据部分
40 | ChannelBuffer totalBuffer;
41 | ByteBuffer headBuffer = ByteBuffer
42 | .allocate(SysConstants.PROTOCOL_HEADER_LENGTH);
43 | if (dataBuffer != null) {
44 | int length = dataBuffer.readableBytes()
45 | + SysConstants.PROTOCOL_HEADER_LENGTH; // 总的包长
46 | headBuffer.putInt(length);// 消息长度
47 | char[] tc = Character.toChars(request.getVersion());
48 | headBuffer.putChar(tc[0]);// 消息version, 1个字节
49 | tc = Character.toChars(request.getFlag());
50 | headBuffer.putChar(tc[0]);// 标记: 分拆数据包,压缩
51 | tc = Character.toChars(request.getServiceId());
52 | headBuffer.putChar(tc[0]); // 服务号, 后端这边固定1000
53 | tc = Character.toChars(request.getCommandId());
54 | headBuffer.putChar(tc[0]); // 命令号
55 | tc = Character.toChars(request.getError());
56 | headBuffer.putChar(tc[0]); // 服务器错误
57 | tc = Character.toChars(request.getReserved());
58 | headBuffer.putChar(tc[0]); // 保留,可用于序列号等
59 | headBuffer.flip(); // 这里需要重置一下游标和长度
60 |
61 | totalBuffer = ChannelBuffers.dynamicBuffer();
62 | totalBuffer.writeBytes(headBuffer);
63 | if (length > SysConstants.PROTOCOL_HEADER_LENGTH) { // 数据部分长度不为0才写入
64 | int dataLength = dataBuffer.readableBytes();
65 | byte[] data = new byte[dataLength];
66 | dataBuffer.readBytes(data);
67 | totalBuffer.writeBytes(data);
68 | }
69 | } else {
70 | int length = SysConstants.PROTOCOL_HEADER_LENGTH; // 总的包长
71 | headBuffer.putInt(length);// 消息长度
72 | headBuffer.put((byte) request.getVersion());// 消息version, 1个字节
73 | headBuffer.put((byte) request.getFlag());// 标记: 分拆数据包,压缩
74 | char[] tc = Character.toChars(request.getServiceId());
75 | headBuffer.putChar(tc[0]); // 服务号, 后端这边固定1000
76 | tc = Character.toChars(request.getCommandId());
77 | headBuffer.putChar(tc[0]); // 命令号 TODO
78 | tc = Character.toChars(request.getError());
79 | headBuffer.putChar(tc[0]); // 服务器错误
80 | tc = Character.toChars(request.getReserved());
81 | headBuffer.putChar(tc[0]); // 保留,可用户如序列号等
82 | headBuffer.flip(); // 这里需要重置一下游标和长度
83 |
84 | totalBuffer = ChannelBuffers.copiedBuffer(headBuffer);
85 | }
86 | // logger.info("encode serviceId : " + request.getServiceId()
87 | // + " commandId: " + request.getCommandId());
88 | Channels.write(ctx, e.getFuture(), totalBuffer);
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/util/HttpUtil.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.Map.Entry;
9 |
10 | import org.apache.http.NameValuePair;
11 | import org.apache.http.client.config.RequestConfig;
12 | import org.apache.http.client.methods.CloseableHttpResponse;
13 | import org.apache.http.client.methods.HttpGet;
14 | import org.apache.http.client.methods.HttpPost;
15 | import org.apache.http.client.utils.URLEncodedUtils;
16 | import org.apache.http.entity.StringEntity;
17 | import org.apache.http.impl.client.CloseableHttpClient;
18 | import org.apache.http.impl.client.HttpClients;
19 | import org.apache.http.message.BasicNameValuePair;
20 | import org.apache.http.util.EntityUtils;
21 |
22 | import com.alibaba.druid.util.StringUtils;
23 | import com.mogujie.ares.lib.logger.Logger;
24 | import com.mogujie.ares.lib.logger.LoggerFactory;
25 | import com.mogujie.ares.lib.net.IMHttpResponse;
26 |
27 | public class HttpUtil {
28 |
29 | private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
30 |
31 | public static IMHttpResponse get(String url) {
32 | IMHttpResponse response = new IMHttpResponse();
33 | response.setStatusCode(404);
34 | if(StringUtils.isEmpty(url)) {
35 | return response;
36 | }
37 | int statusCode = 404;
38 | CloseableHttpClient httpClient = null;
39 | HttpGet httpGet = null;
40 | try {
41 | httpClient = HttpClients.createDefault();
42 | httpGet = new HttpGet(url);//HTTP Get请求(POST雷同)
43 | RequestConfig requestConfig = RequestConfig
44 | .custom()
45 | .setSocketTimeout(2000)
46 | .setConnectTimeout(2000)
47 | .build();//设置请求和传输超时时间
48 | httpGet.setConfig(requestConfig);
49 | CloseableHttpResponse hresp = httpClient.execute(httpGet);//执行请求
50 | String responseString = EntityUtils.toString(hresp.getEntity());
51 | response.setStatusCode(hresp.getStatusLine().getStatusCode());
52 | response.setResponseBody(responseString);
53 | return response;
54 | } catch (Exception e) {
55 | logger.error("error code: " + statusCode, e);
56 | } finally {
57 | if(httpGet != null) {
58 | httpGet.releaseConnection();
59 | }
60 | }
61 | return response;
62 | }
63 |
64 | @SuppressWarnings("rawtypes")
65 | public static IMHttpResponse post(String url) {
66 | return post(url, new HashMap());
67 | }
68 |
69 | @SuppressWarnings({ "rawtypes", "unchecked" })
70 | public static IMHttpResponse post(String url, Map params) {
71 | IMHttpResponse response = new IMHttpResponse();
72 | if(StringUtils.isEmpty(url)) {
73 | response.setStatusCode(404);
74 | return response;
75 | }
76 |
77 | CloseableHttpClient httpClient = null;
78 | HttpPost httpPost = null;
79 | try {
80 | httpPost = new HttpPost(url);//HTTP Get请求(POST雷同)
81 | List postData = new ArrayList();
82 | Iterator piter = params.entrySet().iterator();
83 | while(piter.hasNext()) {
84 | Entry entry = piter.next();
85 | postData.add(new BasicNameValuePair(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())));
86 | }
87 | StringEntity entity = new StringEntity(URLEncodedUtils.format(postData, "UTF-8"));
88 | httpPost.setEntity(entity);
89 | RequestConfig requestConfig = RequestConfig
90 | .custom()
91 | .setSocketTimeout(2000)
92 | .setConnectTimeout(2000)
93 | .build();//设置请求和传输超时时间
94 | httpPost.setConfig(requestConfig);
95 | httpClient = HttpClients.createDefault();
96 | CloseableHttpResponse hresp = httpClient.execute(httpPost);//执行请求
97 | String responseString = EntityUtils.toString(hresp.getEntity());
98 | response.setStatusCode(hresp.getStatusLine().getStatusCode());
99 | response.setResponseBody(responseString);
100 | return response;
101 | } catch(Exception e) {
102 | logger.error("url: " + url, e);
103 | } finally {
104 | if(httpPost != null) {
105 | httpPost.releaseConnection();
106 | }
107 | }
108 | return response;
109 | }
110 |
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/storage/DBPool.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.storage;
2 |
3 | import java.sql.Connection;
4 | import java.sql.ResultSet;
5 | import java.sql.SQLException;
6 | import java.sql.Statement;
7 |
8 | import com.alibaba.druid.pool.DruidDataSource;
9 | import com.mogujie.ares.lib.logger.Logger;
10 | import com.mogujie.ares.lib.logger.LoggerFactory;
11 |
12 | /**
13 | *
14 | * @Description: db连接池
15 | * @author shitou - shitou[at]mogujie.com
16 | * @date 2013-7-21 下午5:42:55
17 | *
18 | */
19 | public class DBPool
20 | {
21 |
22 | private static final Logger logger = LoggerFactory.getLogger(DBPool.class);
23 |
24 | // private BoneCP dbConnectionPool = null;
25 |
26 | // private BoneCPConfig dbBoneCPConfig = null;
27 |
28 | private DruidDataSource druid;
29 |
30 | private String jdbcUrl;
31 |
32 | private String jdbcUsername;
33 |
34 | private String jdbcPassword;
35 |
36 | private String driverClass = "com.mysql.jdbc.Driver";
37 |
38 | private int initConnectionCount = 5;
39 |
40 | private int maxActiveConnection = 18;
41 |
42 |
43 | public DBPool(String jdbcUrl, String username, String password)
44 | {
45 | this.jdbcUrl = jdbcUrl;
46 | this.jdbcUsername = username;
47 | this.jdbcPassword = password;
48 | }
49 |
50 | /**
51 | * 初始化
52 | */
53 | // public void initial()
54 | // {
55 | // //load configure
56 | //// if(dbBoneCPConfig == null)
57 | //// {
58 | //// dbBoneCPConfig = new BoneCPConfig();
59 | //// dbBoneCPConfig.setJdbcUrl(this.jdbcUrl);
60 | //// dbBoneCPConfig.setUsername(this.jdbcUsername);
61 | //// dbBoneCPConfig.setPassword(this.jdbcPassword);
62 | //// dbBoneCPConfig.setMinConnectionsPerPartition(Constants.DB_MIN_CONNECTIONS_PER_PARTITION);
63 | //// dbBoneCPConfig.setMaxConnectionsPerPartition(Constants.DB_MAX_CONNECTIONS_PER_PARTITION);
64 | //// dbBoneCPConfig.setIdleConnectionTestPeriod(Constants.DB_IDLE_CONNECTION_TEST_PERIOD, TimeUnit.SECONDS);
65 | //// dbBoneCPConfig.setPartitionCount(Constants.DB_PARTITION_COUNT);
66 | //// }
67 | // }
68 |
69 | /**
70 | * 启动
71 | * @throws SQLException
72 | */
73 | public void launchDBPool() throws SQLException
74 | {
75 | if(druid == null)
76 | {
77 | // initial();
78 | try {
79 | // dbConnectionPool = new BoneCP(dbBoneCPConfig);
80 | druid = new DruidDataSource();
81 | druid.setDriverClassName(driverClass);
82 | druid.setUsername(jdbcUsername);
83 | druid.setPassword(jdbcPassword);
84 | druid.setUrl(jdbcUrl);
85 | druid.setMinIdle(maxActiveConnection);
86 | druid.setInitialSize(initConnectionCount);
87 | druid.setMaxActive(maxActiveConnection);
88 | druid.setFilters("stat");
89 | druid.setTestWhileIdle(true);
90 | druid.setTestOnBorrow(false);
91 | druid.setTestOnReturn(false);
92 | druid.setMaxWait(60000);
93 | druid.setPoolPreparedStatements(false);
94 | druid.setValidationQuery("SELECT 'x'");
95 |
96 | druid.init();
97 | } catch (SQLException e) {
98 | logger.error("", e);
99 | throw e;
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * 关闭
106 | */
107 | public void shutDownDBPool()
108 | {
109 | if(druid != null)
110 | {
111 | //dbConnectionPool.shutdown();
112 | druid.close();
113 | }
114 | }
115 |
116 | /**
117 | * 从Pool中获取一个链接
118 | */
119 | public Connection getConnection()
120 | {
121 | Connection connection = null;
122 | try {
123 | // connection = dbConnectionPool.getConnection();
124 | connection = druid.getConnection();
125 | } catch (SQLException e) {
126 | logger.error("", e);
127 | }
128 | return connection;
129 | }
130 |
131 | /**
132 | * 快捷关闭
133 | * @param connection
134 | * @paramlogger.error("", e);
135 | */
136 | public void shortCutClose(Connection connection, Statement stat, ResultSet rs)
137 | {
138 | try {
139 | if(rs != null && !rs.isClosed()) {
140 | rs.close();
141 | }
142 | if(stat != null && !stat.isClosed()) {
143 | stat.close();
144 | }
145 | if(connection != null && !connection.isClosed()) {
146 | connection.close();
147 | }
148 | } catch (SQLException e) {
149 | logger.error("", e);
150 | }
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/CacheManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import java.util.Iterator;
4 | import java.util.Map;
5 | import java.util.Map.Entry;
6 | import java.util.Properties;
7 | import java.util.concurrent.ConcurrentHashMap;
8 |
9 | import com.mogujie.ares.lib.storage.CachePool;
10 |
11 | import redis.clients.jedis.Jedis;
12 |
13 | /**
14 | *
15 | * @Description: 缓存连接相关的管理类,针对不同业务会有多个连接池,单例
16 | * @author shitou - shitou[at]mogujie.com
17 | * @date 2013-7-21 下午5:45:18
18 | *
19 | */
20 | public class CacheManager
21 | {
22 | private ConcurrentHashMap cachePool = new ConcurrentHashMap(); // 多个池子
23 |
24 | private static CacheManager cacheManagerInstance = new CacheManager();
25 |
26 | private Properties cacheProperties;
27 |
28 | private boolean isLanuch = false;
29 |
30 | public static CacheManager getInstance()
31 | {
32 | if(cacheManagerInstance == null)
33 | {
34 | cacheManagerInstance = new CacheManager();
35 | }
36 | return cacheManagerInstance;
37 | }
38 |
39 | private CacheManager()
40 | {
41 | this.cacheProperties = ConfigureManager.getInstance().getCacheConfig();
42 | launch();
43 | }
44 |
45 | /**
46 | * @Description: 初始化
47 | */
48 | private void launch()
49 | {
50 | if( ! isLanuch)
51 | {
52 | //shutDown();
53 |
54 | String needLaunchPoolInstance = cacheProperties.getProperty("instances");
55 | String[] needLaunchPools = needLaunchPoolInstance.split(",");
56 | for(String instance : needLaunchPools)
57 | {
58 | //获取配置
59 | String redisHost = cacheProperties.getProperty(instance + "_host");
60 | Integer redisPort = Integer.valueOf(cacheProperties.getProperty(instance + "_port"));
61 | Integer redisDB = Integer.valueOf(cacheProperties.getProperty(instance + "_db"));
62 |
63 | //实例化,启动,并塞入Hashmap中
64 | CachePool cachePoolInstance = new CachePool(redisHost, redisPort, redisDB);
65 | cachePoolInstance.launch();
66 | cachePool.put(instance, cachePoolInstance);
67 | }
68 | isLanuch = true;
69 | }
70 | }
71 |
72 | /**
73 | * @Description: 关闭所有连接池
74 | */
75 | public void shutDown()
76 | {
77 | if(this.cachePool.size() > 0)
78 | {
79 | Iterator> iterator = this.cachePool.entrySet().iterator();
80 | while(iterator.hasNext())
81 | {
82 | Map.Entry hashEntry = iterator.next();
83 | CachePool poolInstance = hashEntry.getValue();
84 | poolInstance.destory();
85 |
86 | cachePool.remove(hashEntry.getKey());
87 | }
88 |
89 | cachePool.clear();
90 | }
91 | }
92 |
93 | /**
94 | * @Description: 从指定的链接池中获取一个链接
95 | * @param poolName 连接池的名字
96 | * @return
97 | */
98 | public Jedis getResource(CachePoolName poolName) {
99 | String strName = poolName.toString();
100 |
101 | return getJedisResource(strName);
102 | }
103 |
104 | /**
105 | * @Description: 释放一个链接
106 | * @param poolName 连接所在的连接池
107 | * @param jedis 连接
108 | */
109 | public void returnResource(CachePoolName poolName, Jedis jedis) {
110 | if(jedis == null) {
111 | return ;
112 | }
113 |
114 | String strName = poolName.toString();
115 | returnJedisResource(strName, jedis);
116 | }
117 |
118 | /**
119 | *
120 | * @Description: 具体释放连接操作
121 | * @param poolName
122 | * @param jedisInstance
123 | */
124 | private void returnJedisResource(String poolName, Jedis jedisInstance) {
125 | CachePool pool = cachePool.get(poolName);
126 | if(pool != null) {
127 | pool.returnResource(jedisInstance);
128 | }
129 | }
130 |
131 | /**
132 | *
133 | * @Description: 具体获取连接操作
134 | * @param instanceName
135 | * @return
136 | */
137 | private Jedis getJedisResource(String instanceName) {
138 | Jedis jedisResource = null;
139 | CachePool pool = cachePool.get(instanceName);
140 | if(pool != null) {
141 | jedisResource = pool.getResource();
142 | }
143 | return jedisResource;
144 | }
145 |
146 | /**
147 | *
148 | * @Description: 连接池的名字
149 | * @author ziye - ziye[at]mogujie.com
150 | * @date 2013-7-21 下午6:37:06
151 | *
152 | */
153 | public enum CachePoolName {
154 | counter, // 所有计数器
155 | unread, // 未读计数器
156 | // cinfo, //用户信息(消息通知用的).
157 | // cinfo_push, // 推送cinfo
158 | group_counter
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/util/MoguUtil.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.util;
2 |
3 | import java.security.MessageDigest;
4 | import java.security.NoSuchAlgorithmException;
5 | import java.util.HashMap;
6 | import java.util.HashSet;
7 | import java.util.Map;
8 | import java.util.Set;
9 |
10 | import com.mogujie.ares.data.User;
11 | import com.mogujie.ares.lib.net.DataBuffer;
12 |
13 | /*
14 | * 蘑菇街IM工具类
15 | */
16 | public final class MoguUtil {
17 |
18 | /*
19 | * 判断是否是管理员
20 | */
21 | public boolean isAdmin(User user) {
22 | return (user == null) ? false : isAdmin(user.getUserType());
23 | }
24 |
25 | /*
26 | * 判断是否是管理员
27 | */
28 | public boolean isAdmin(int type) {
29 | return (type == 1) ? true : false;
30 | }
31 |
32 | /*
33 | * 对int数组去重
34 | *
35 | * @param array 需要去重的数组
36 | */
37 | public static int[] distinct(int[] array) {
38 | Set intSet = new HashSet();
39 | int intVal;
40 | int length = array.length;
41 | for (int i = 0; i < length; i++) {
42 | intVal = array[i];
43 | if (!intSet.contains(intVal)) {
44 | intSet.add(intVal);
45 | }
46 | }
47 | Integer[] uniqArray = new Integer[intSet.size()];
48 | intSet.toArray(uniqArray);
49 | int[] ints = new int[uniqArray.length];
50 | for (int i = 0; i < uniqArray.length; i++) {
51 | ints[i] = uniqArray[i];
52 | }
53 | return ints;
54 | }
55 |
56 | /*
57 | * 对int数组去重并返回map对象
58 | *
59 | * @param array 需要去重的数组
60 | */
61 | public static Map distinctToMap(int[] array) {
62 | Set intSet = new HashSet();
63 | int intVal;
64 | int length = array.length;
65 | for (int i = 0; i < length; i++) {
66 | intVal = array[i];
67 | if (!intSet.contains(intVal)) {
68 | intSet.add(intVal);
69 | }
70 | }
71 | Integer[] uniqArray = new Integer[intSet.size()];
72 | intSet.toArray(uniqArray);
73 | Map mapIds = new HashMap();
74 | for (int i = 0; i < uniqArray.length; i++) {
75 | mapIds.put(uniqArray[i], uniqArray[i]);
76 | }
77 | return mapIds;
78 | }
79 |
80 | /*
81 | * 获得DB中参数?占位符
82 | *
83 | * @parma number 参数个数
84 | */
85 | public static String getArgsHolder(int number) {
86 | return getArgsHolder("?", ",", number);
87 | }
88 |
89 | /*
90 | * 获得DB中参数?占位符
91 | *
92 | * @param hoder 占位的字符
93 | *
94 | * @param split 分隔的字符
95 | *
96 | * @parma number 参数个数
97 | */
98 | public static String getArgsHolder(String hoder, String split, int number) {
99 | StringBuffer buffer = new StringBuffer("");
100 | if (number <= 0) {
101 | return buffer.toString();
102 | }
103 |
104 | for (int i = 0; i < number; i++) {
105 | buffer.append(split + hoder);
106 | }
107 | return buffer.toString().substring(1);
108 |
109 | }
110 |
111 | /**
112 | *
113 | * @Description: md5加密
114 | * @param str
115 | * @return
116 | * @throws NoSuchAlgorithmException
117 | */
118 | public static String md5(String str) throws NoSuchAlgorithmException {
119 |
120 | MessageDigest tool = MessageDigest.getInstance("MD5");
121 | tool.update(str.getBytes());
122 | byte[] bytes = tool.digest();
123 | StringBuffer sb = new StringBuffer();
124 | for (int i = 0; i < bytes.length; i++) { // 字节数组转换成十六进制字符串,形成最终的密文
125 | int v = bytes[i] & 0xff;
126 | if (v < 16) {
127 | sb.append(0);
128 | }
129 | sb.append(Integer.toHexString(v));
130 | }
131 |
132 | return sb.toString();
133 | }
134 |
135 | /*
136 | * 将请求附带的信息写回
137 | */
138 | public static DataBuffer writeAttachments(DataBuffer buffer,
139 | DataBuffer attachment) {
140 | if (null != buffer && null != attachment) {
141 | buffer.writeInt(attachment.readableBytes());
142 | if (attachment.readableBytes() > 0) {
143 | buffer.writeDataBuffer(attachment);
144 | }
145 | }
146 |
147 | return buffer;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/configure/Configure.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.configure;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.Properties;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | import org.dom4j.DocumentException;
12 | import org.dom4j.Node;
13 | import org.dom4j.io.SAXReader;
14 |
15 | import com.mogujie.ares.lib.logger.Logger;
16 | import com.mogujie.ares.lib.logger.LoggerFactory;
17 |
18 | /**
19 | * 配置,单利
20 | * @author stone
21 | *
22 | */
23 | public class Configure
24 | {
25 | public static final Logger logger = LoggerFactory.getLogger(Configure.class);
26 |
27 | public Properties systemConfig;
28 |
29 | public Properties dbConfig;
30 |
31 | public Properties cacheConfig;
32 |
33 | public ConcurrentHashMap> timerConfig;
34 |
35 | public Router actionRouter;
36 |
37 | public Configure()
38 | {
39 | systemConfig = new Properties();
40 | dbConfig = new Properties();
41 | cacheConfig = new Properties();
42 | timerConfig = new ConcurrentHashMap>();
43 | actionRouter = Router.getInstance();
44 | }
45 |
46 | public Properties getSystemConfig()
47 | {
48 | return systemConfig;
49 | }
50 |
51 | public Properties getDBConfig()
52 | {
53 | return dbConfig;
54 | }
55 |
56 | public Properties getCacheConfig()
57 | {
58 | return cacheConfig;
59 | }
60 |
61 | public Router getActionRouter() {
62 | return actionRouter;
63 | }
64 |
65 | public ConcurrentHashMap> getTimerConfig()
66 | {
67 | return timerConfig;
68 | }
69 |
70 | /**
71 | * 载入所有配置
72 | * @throws Exception
73 | */
74 | public void loadConfigs() throws Exception {
75 | loadSystemConfig(); // 系统配置
76 | loadDBConfig();
77 | loadCacheConfig();
78 | loadActionConfig(); // action相关的路由配置
79 | loadTimerConfig(); // 计划任务脚本配置
80 | }
81 |
82 | public void loadSystemConfig()
83 | {
84 | propertyConfigLoader("system.properties", systemConfig);
85 | }
86 |
87 | public void loadDBConfig()
88 | {
89 | propertyConfigLoader(systemConfig.getProperty("com.mogujie.ares.config.file.db"), dbConfig);
90 | }
91 |
92 | public void loadCacheConfig()
93 | {
94 | propertyConfigLoader(systemConfig.getProperty("com.mogujie.ares.config.file.cache"), cacheConfig);
95 | }
96 |
97 | public void loadActionConfig() throws Exception {
98 | actionRouter.load(systemConfig.getProperty("com.mogujie.ares.config.file.route"));
99 | }
100 |
101 | public void loadTimerConfig()
102 | {
103 | taskXMLConfigLoader(systemConfig.getProperty("com.mogujie.ares.config.file.timer"), timerConfig);
104 | }
105 |
106 | public void propertyConfigLoader(String configFilePath, Properties config)
107 | {
108 | if(configFilePath.length() == 0)
109 | {
110 | return ;
111 | }
112 |
113 | InputStream configFileStream = Configure.class.getClassLoader()
114 | .getResourceAsStream(configFilePath);
115 | try {
116 | config.load(configFileStream);
117 | } catch (IOException e) {
118 | logger.error("", e);
119 | }
120 | }
121 |
122 | /**
123 | * 载入xml格式的计划任务脚本的配置
124 | *
125 | * @param configFilePath
126 | * @param config
127 | */
128 | @SuppressWarnings({"unchecked"})
129 | public void taskXMLConfigLoader(String configFilePath, Map> config)
130 | {
131 | if(configFilePath.length() == 0)
132 | {
133 | return ;
134 | }
135 | // String configFileFullPath = System.getProperty("user.dir") +
136 | // "/src/main/resources/" + configFilePath;
137 | // if( ! new File(configFileFullPath).exists())
138 | // {
139 | // return ;
140 | // }
141 |
142 | InputStream is = this.getClass().getResourceAsStream("/" + configFilePath);
143 | if(is == null) {
144 | return;
145 | }
146 | SAXReader reader = new SAXReader();
147 | try {
148 | org.dom4j.Document configDocument = reader.read(is);
149 | List nodes = configDocument.selectNodes("//tasks/task");
150 | for(Node node : nodes)
151 | {
152 | Node enable = node.selectSingleNode("enable");
153 | Node name = node.selectSingleNode("name");
154 | Node initdelay = node.selectSingleNode("initdelay");
155 | Node periodordelay = node.selectSingleNode("periodordelay");
156 | Node taskType = node.selectSingleNode("taskType");
157 |
158 | HashMap theTaskConfig = new HashMap();
159 | theTaskConfig.put("enable", enable.getText());
160 | theTaskConfig.put("initdelay", initdelay.getText());
161 | theTaskConfig.put("periodordelay", periodordelay.getText());
162 | theTaskConfig.put("taskType", taskType.getText());
163 |
164 | config.put(name.getText(), theTaskConfig);
165 | }
166 | } catch (DocumentException e) {
167 | logger.error("", e);
168 | }
169 | }
170 |
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/DBManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import java.sql.Connection;
4 | import java.sql.ResultSet;
5 | import java.sql.SQLException;
6 | import java.sql.Statement;
7 | import java.util.Iterator;
8 | import java.util.Properties;
9 | import java.util.concurrent.ConcurrentHashMap;
10 |
11 | import com.mogujie.ares.lib.logger.Logger;
12 | import com.mogujie.ares.lib.logger.LoggerFactory;
13 | import com.mogujie.ares.lib.storage.DBPool;
14 |
15 | /**
16 | *
17 | * @Description: db连接池管理类,单例
18 | * @author shitou - shitou[at]mogujie.com
19 | * @date 2013-7-21 下午7:04:05
20 | *
21 | */
22 | public class DBManager {
23 | private static final Logger logger = LoggerFactory
24 | .getLogger(DBManager.class);
25 |
26 | // 连接池集合
27 | private ConcurrentHashMap dbPoolMap = new ConcurrentHashMap();
28 |
29 | // db配置
30 | private Properties dbProperties;
31 |
32 | // 是否已加载
33 | private boolean isLaunch = false;
34 |
35 | // 单例
36 | private static DBManager dbManagerInstance = getInstance();
37 |
38 | public static DBManager getInstance() {
39 | if (dbManagerInstance == null) {
40 | try {
41 | dbManagerInstance = new DBManager();
42 | } catch (SQLException e) {
43 | logger.error("", e);
44 | }
45 | }
46 | return dbManagerInstance;
47 | }
48 |
49 | private DBManager() throws SQLException {
50 | dbProperties = ConfigureManager.getInstance().getDBConfig();
51 | launch();
52 | }
53 |
54 | /**
55 | *
56 | * @throws SQLException
57 | * @Description: 初始化加载
58 | */
59 | private void launch() throws SQLException {
60 | if (!isLaunch) {
61 | // shutDown();
62 |
63 | String shutDownInstances = dbProperties.getProperty("instances");
64 | String[] instances = shutDownInstances.split(",");
65 | for (String instanceName : instances) {
66 | if (instanceName.length() == 0) {
67 | continue;
68 | }
69 | String jdbcUrl = dbProperties
70 | .getProperty(instanceName + "_url");
71 | String jdbcUsername = dbProperties.getProperty(instanceName
72 | + "_username");
73 | String jdbcPassword = dbProperties.getProperty(instanceName
74 | + "_password");
75 | DBPool newDBPool = new DBPool(jdbcUrl, jdbcUsername,
76 | jdbcPassword);
77 | newDBPool.launchDBPool();
78 |
79 | dbPoolMap.put(instanceName, newDBPool);
80 | }
81 | isLaunch = true;
82 | }
83 |
84 | }
85 |
86 | /**
87 | *
88 | * @Description: 关闭所有连接池
89 | */
90 | public void shutDown() {
91 | if (dbPoolMap.size() > 0) {
92 | Iterator iterator = dbPoolMap.keySet().iterator();
93 | while (iterator.hasNext()) {
94 | DBPool pool = dbPoolMap.remove(iterator.next());
95 | if (pool != null) {
96 | pool.shutDownDBPool();
97 | }
98 | }
99 | dbPoolMap.clear();
100 | }
101 | }
102 |
103 | /**
104 | *
105 | * @Description: 获得DB连接
106 | * @return 指定的链接池中的一个连接
107 | */
108 | public Connection getConnection(DBPoolName name) {
109 | Connection connection = null;
110 | DBPool dbPool = dbPoolMap.get(name.toString());
111 | if (dbPool != null) {
112 | connection = dbPool.getConnection();
113 | }
114 | return connection;
115 | }
116 |
117 | /**
118 | *
119 | * @Description: 释放连接
120 | * @param poolName
121 | * 连接所在的连接池
122 | * @param connection
123 | * 连接
124 | */
125 | public void closeConnection(DBPoolName poolName, Connection connection) {
126 | DBPool pool = dbPoolMap.get(poolName.toString());
127 | if (pool != null) {
128 | pool.shortCutClose(connection, null, null);
129 | }
130 | }
131 |
132 | /**
133 | *
134 | * @Description: 集中释放各种资源
135 | * @param poolName
136 | * 连接所在的连接池的名字
137 | * @param connection
138 | * 连接
139 | * @param statement
140 | * @param resultSet
141 | */
142 | public void release(DBPoolName poolName, Connection connection,
143 | Statement statement, ResultSet resultSet) {
144 | DBPool pool = dbPoolMap.get(poolName.toString());
145 | if (pool != null) {
146 | pool.shortCutClose(connection, statement, resultSet);
147 | }
148 | }
149 |
150 | /**
151 | *
152 | * @Description: 连接池的名字,定义一个枚举,以免传错
153 | * @author ziye - ziye[at]mogujie.com
154 | * @date 2013-7-22 上午11:45:27
155 | *
156 | */
157 | public enum DBPoolName {
158 | macim_master, macim_slave
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/RequestParams.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend;
2 |
3 | import java.util.Iterator;
4 | import java.util.LinkedHashMap;
5 | import java.util.Map;
6 |
7 | import org.jboss.netty.channel.ChannelHandlerContext;
8 |
9 | import com.mogujie.ares.lib.net.DataBuffer;
10 |
11 | /**
12 | *
13 | * @Description: 请求参数的描述和控制类,负责将请求数据包中的二进制数据解析成具体的变量
14 | * @author ziye - ziye[at]mogujie.com
15 | * @date 2013-7-21 上午11:41:37
16 | *
17 | */
18 | @SuppressWarnings({"rawtypes"})
19 | public class RequestParams {
20 |
21 | // 原生的描述参数的数据,参数名 => 参数类型,详细请见配置route.xml
22 | private Map metaData = new LinkedHashMap();
23 |
24 | // 参数名和java类型的对应
25 | private Map paramsType = new LinkedHashMap();
26 |
27 | /**
28 | *
29 | * @Description: 添加单个参数
30 | * @param name 参数名称
31 | * @param typeName 参数类型名称
32 | * @param typeClazz 参数的java类型
33 | */
34 | public void add(String name, String typeName, Class typeClazz) {
35 | metaData.put(name, typeName);
36 | paramsType.put(name, typeClazz);
37 | }
38 |
39 | public Map getParamsType() {
40 | return paramsType;
41 | }
42 |
43 | public void setParamsType(Map paramsType) {
44 | this.paramsType = paramsType;
45 | }
46 |
47 | public Map getMetaData() {
48 | return metaData;
49 | }
50 |
51 | public void setMetaData(Map metaData) {
52 | this.metaData = metaData;
53 | }
54 |
55 | /**
56 | * @Description: 将请求数据包中的二进制数据解析成具体的变量
57 | * @param dataBuffer
58 | * @param actionContext
59 | * @return
60 | * @throws Exception
61 | * NOTE: 最后一个参数 version不需要客户端手动传的.处理参数的时候,自动从请求的报头里取的.
62 | */
63 | public Object[] decode(DataBuffer dataBuffer, ChannelHandlerContext context, ActionContext actionContext,int version) throws Exception {
64 | Object[] values = null;
65 |
66 | if(metaData == null || metaData.size() <= 0) {
67 | values = new Object[0];
68 | return values;
69 | }
70 |
71 | values = new Object[metaData.size()];
72 | Iterator it = metaData.keySet().iterator();
73 | String type;
74 | String name;
75 | int i = 0;
76 | while(it.hasNext() && i!=metaData.size()-1) {
77 | name = it.next();
78 | type = metaData.get(name);
79 | Object value = null;
80 | if(name.startsWith("${") && name.endsWith("}")) { // 系统数据
81 | // 这里特判
82 | if(name.equals("${channel.remoteAddress}")) { // 客户端的地址信息
83 | value = context.getChannel().getRemoteAddress().toString();
84 | } else if(name.equals("${action.requestType}")) {
85 | value = actionContext.getRequestType();
86 | }
87 | // TO BE CONTINUED....
88 | } else {
89 | if("int".equals(type)) {
90 | value = dataBuffer.readInt();
91 | } else if("char".equals(type)) {
92 | value = dataBuffer.readChar();
93 | } else if("byte".equals(type)) {
94 | value = dataBuffer.readByte();
95 | } else if("String".equals(type)) {
96 | value = dataBuffer.readString();
97 | } else if("int-array".equals(type)) { // int数组
98 | value = dataBuffer.readIntArray();
99 | } else if("byte-array".equals(type)) { // byte数组
100 | value = dataBuffer.readByteArray();
101 | } else if("string-array".equals(type)) { // String数组
102 | value = dataBuffer.readStringArray();
103 | } else if("DataBuffer".equals(type)) { // 直接传DataBuffer,业务方自己解析,太霸道了
104 | value = dataBuffer.readDataBuffer();
105 | }else {
106 | throw new Exception("不支持的类型");
107 | }
108 | }
109 | values[i++] = value;
110 | }
111 | values[i++] = version;
112 | return values;
113 | }
114 |
115 | /**
116 | * @throws Exception 当传入的参数类型不支持时返回这个错误,比如Map什么的神类型
117 | * @Description: 添加一个方法的参数,要按照从左到右的顺序添加,不然会有问题
118 | * @param @param paramsMap 参数配置的map,参数名 => 类型名称
119 | * @return Class[] 直接返回参数类型对应的java类型数组,顺序与传入时(配置)一致
120 | * @throws
121 | */
122 | public Class[] addParams(Map paramsMap) throws Exception {
123 | Class[] paramTypes = new Class[paramsMap.size()];
124 | Iterator it = paramsMap.keySet().iterator();
125 | int j = 0;
126 | Class typeClazz = null;
127 | while(it.hasNext()) {
128 | String name = it.next();
129 | String typeName = paramsMap.get(name);
130 | if("int".equals(typeName)) {
131 | typeClazz = int.class;
132 | } else if("String".equals(typeName)) {
133 | typeClazz = String.class;
134 | } else if("byte".equals(typeName)) { // byte,和int分开是因为数据包中int占4个字节,byte只占一个字节
135 | typeClazz = byte.class;
136 | } else if("int-array".equals(typeName)) { // int数组
137 | typeClazz = int[].class;
138 | } else if("byte-array".equals(typeName)) { // byte数组
139 | typeClazz = byte[].class;
140 | } else if("string-array".equals(typeName)) { // string数组
141 | typeClazz = String[].class;
142 | } else if("DataBuffer".equals(typeName)) { // 鬼畜的dataBuffer
143 | typeClazz = DataBuffer.class;
144 | } else {
145 | throw new Exception(typeName + "是神马!");
146 | }
147 | paramTypes[j++] = typeClazz;
148 | add(name, typeName, typeClazz);
149 | }
150 |
151 | return paramTypes;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/DelayUpdateMonitor.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.util.Map;
4 | import java.util.concurrent.ConcurrentHashMap;
5 |
6 | import com.mogujie.ares.lib.logger.Logger;
7 | import com.mogujie.ares.lib.logger.LoggerFactory;
8 | import com.mogujie.ares.model.GroupModel;
9 | import com.mogujie.ares.model.RelationshipModel;
10 |
11 | public class DelayUpdateMonitor {
12 | public static final int DEFAULT_PACKET_SEND_MONTOR_INTERVAL = 30 * 1000;
13 |
14 | private Monitor mMonitor;
15 | private ConcurrentHashMap mGroupForUpdateMap = new ConcurrentHashMap();
16 | private ConcurrentHashMap mPersonalForUpdateMap = new ConcurrentHashMap();
17 | private volatile boolean mNeedStop;
18 | private volatile boolean mStarted = false;
19 | private static final Logger logger = LoggerFactory
20 | .getLogger(MessageContent.class);
21 | private static DelayUpdateMonitor instance;
22 |
23 | public static DelayUpdateMonitor getInstance() {
24 | if (null == instance) {
25 | instance = new DelayUpdateMonitor();
26 | }
27 | return instance;
28 | }
29 |
30 | private DelayUpdateMonitor() {
31 | mMonitor = new Monitor("TT-Delay-Update-Monitor",
32 | DEFAULT_PACKET_SEND_MONTOR_INTERVAL);
33 | }
34 |
35 | public synchronized void start() {
36 | if (mStarted)
37 | return;
38 | mNeedStop = false;
39 | mMonitor.start();
40 | mStarted = true;
41 | }
42 |
43 | public synchronized void stop() {
44 | if (mNeedStop) {
45 | return;
46 | }
47 | mNeedStop = true;
48 | mStarted = false;
49 | }
50 |
51 | public void AddPersonalUpdate(int uA, int uB, int time) {
52 | int small = uA < uB ? uA : uB;
53 | int big = uA + uB - small;
54 | String sKey = small + ":" + big;
55 | logger.info("user " + uA + " and " + uB + "wants to update to :" + time);
56 | synchronized(mPersonalForUpdateMap) {
57 | if (mPersonalForUpdateMap.containsKey(sKey)) {
58 | mPersonalForUpdateMap.remove(sKey);
59 | }
60 | mPersonalForUpdateMap.put(sKey, time);
61 | }
62 | }
63 |
64 | public void AddGroupUpdate(int groupId, int time) {
65 | logger.info("Group " + groupId + "wants to update to :" + time);
66 | synchronized(mGroupForUpdateMap) {
67 | if (mGroupForUpdateMap.containsKey(groupId)) {
68 | mGroupForUpdateMap.remove(groupId);
69 | }
70 | mGroupForUpdateMap.put(groupId, time);
71 | }
72 | }
73 |
74 | private class Monitor extends Thread {
75 |
76 | private int mInterval;
77 | public Monitor(String name, int interval) {
78 | setName(name);
79 | mInterval = interval;
80 | }
81 |
82 | @Override
83 | public void run() {
84 | super.run();
85 | try {
86 | while (!mNeedStop) {
87 | ConcurrentHashMap oldGroupMap = new ConcurrentHashMap();
88 | synchronized(mGroupForUpdateMap) {
89 | ConcurrentHashMap tmpMap;
90 | tmpMap = mGroupForUpdateMap;
91 | mGroupForUpdateMap = oldGroupMap;
92 | oldGroupMap = tmpMap;
93 | }
94 | GroupModel groupModel = GroupModel.getInstance();
95 | for(Map.Entry e: oldGroupMap.entrySet() ){
96 | int groupId = e.getKey();
97 | int time = e.getValue();
98 | groupModel.updateGroupTime(groupId, time);
99 | logger.info("Group " + groupId + " update to :" + time);
100 | }
101 |
102 | ConcurrentHashMap oldPersonalMap = new ConcurrentHashMap();
103 | synchronized(mPersonalForUpdateMap) {
104 | ConcurrentHashMap tmpMap;
105 | tmpMap = mPersonalForUpdateMap;
106 | mPersonalForUpdateMap = oldPersonalMap;
107 | oldPersonalMap = tmpMap;
108 | }
109 |
110 | RelationshipModel relationModel = RelationshipModel.getInstance();
111 | for(Map.Entry e: oldPersonalMap.entrySet() ){
112 | String[] resultString = e.getKey().split(":");
113 | int uA = Integer.parseInt(resultString[0]);
114 | int uB = Integer.parseInt(resultString[1]);
115 | int time = e.getValue();
116 | relationModel.updateRelationShip(uA, uB, time);
117 | logger.info("user " + uA + " and " + uB + " update to :" + time);
118 | }
119 |
120 | sleep(mInterval);
121 | }
122 | } catch (Exception e) {
123 | }
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/FileAction.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.util.List;
4 |
5 | import com.mogujie.ares.data.TransmitFile;
6 | import com.mogujie.ares.extend.BaseAction;
7 | import com.mogujie.ares.lib.logger.Logger;
8 | import com.mogujie.ares.lib.logger.LoggerFactory;
9 | import com.mogujie.ares.lib.net.DataBuffer;
10 | import com.mogujie.ares.model.FileModel;
11 | import com.mogujie.ares.util.MoguUtil;
12 |
13 | /**
14 | *
15 | * @Description: 用户相关的类
16 | * @author ziye - ziye[at]mogujie.com
17 | * @date 2013-8-12 下午4:55:45
18 | *
19 | */
20 | public class FileAction extends BaseAction {
21 |
22 | private static final Logger logger = LoggerFactory
23 | .getLogger(FileAction.class);
24 |
25 | /*
26 | *
27 | * @Description: 存储离线文件的信息
28 | *
29 | * @param fromUserId
30 | *
31 | * @param userIds
32 | *
33 | * @return
34 | */
35 | public DataBuffer addFileRecord(int commandId, int requestId,
36 | int fromUserId, int toUserId, String taskId, String filePath, int fileSize,
37 | int version) {
38 | logger.info("add file recored : " + "fromUserId=" + fromUserId
39 | + ", toUserId=" + toUserId + ", taskId = " + taskId + ", filePath=" + filePath);
40 | DataBuffer buffer;
41 | if (fromUserId <= 0 || toUserId <= 0 || filePath == null
42 | || "".equals(filePath)) {
43 | buffer = new DataBuffer();
44 | buffer.writeInt(requestId); // request uuid
45 | buffer.writeInt(1); // result
46 | buffer.writeInt(commandId); // source commandId
47 | buffer.writeInt(fromUserId); // fromUserId
48 | buffer.writeInt(toUserId); // toUserId
49 | return buffer;
50 | }
51 |
52 | buffer = new DataBuffer();
53 | buffer.writeInt(requestId); // request uuid
54 | boolean isSuccess = false;
55 | isSuccess = FileModel.getInstance().saveFileRecord(fromUserId,
56 | toUserId, taskId, filePath, fileSize);
57 | int result = isSuccess ? 0 : 2;
58 | buffer.writeInt(result); // result
59 | buffer.writeChar((char) commandId); // source commandId
60 | buffer.writeInt(fromUserId); // fromUserId
61 | buffer.writeInt(toUserId); // toUserId
62 | return buffer;
63 | }
64 |
65 | /*
66 | *
67 | * @Description: 删除离线文件的信息
68 | *
69 | * @param fromUserId
70 | *
71 | * @param userIds
72 | *
73 | * @return
74 | */
75 | public DataBuffer removeFileRecord(int commandId, int requestId,
76 | int fromUserId, int toUserId, String taskId, int version) {
77 | logger.info("remove file recored : " + "taskId=" + taskId);
78 | DataBuffer buffer;
79 | if (null == taskId || taskId.isEmpty()) {
80 | buffer = new DataBuffer();
81 | buffer.writeInt(requestId); // request uuid
82 | buffer.writeInt(1); // result
83 | buffer.writeChar((char) commandId);
84 | buffer.writeInt(fromUserId); // fromUserId
85 | buffer.writeInt(toUserId); // toUserId
86 | return buffer;
87 | }
88 |
89 | buffer = new DataBuffer();
90 | buffer.writeInt(requestId); // request uuid
91 | TransmitFile file = FileModel.getInstance().getFileRecord(taskId);
92 | boolean isSuccess = false;
93 | int now = (int) (System.currentTimeMillis() / 1000);
94 | if (file != null && file.getStatus() == 1
95 | && file.getCreated() > now - 7 * 86400) {
96 | isSuccess = FileModel.getInstance().deleteFileRecord(taskId);
97 | }
98 | int result = isSuccess ? 0 : 2;
99 | buffer.writeInt(result); // result
100 | buffer.writeChar((char) commandId);
101 | buffer.writeInt(fromUserId); // fromUserId
102 | buffer.writeInt(toUserId); // toUserId
103 | return buffer;
104 | }
105 |
106 | /*
107 | *
108 | * @Description: 存储或删除立宪文件的信息
109 | *
110 | * @param fromUserId
111 | *
112 | * @param userIds
113 | *
114 | * @return
115 | */
116 | public DataBuffer getFileRecord(int requestUserId, DataBuffer attachment, int version) {
117 | logger.info("-->> getFileRecord ->> get file recored : "
118 | + requestUserId);
119 | DataBuffer buffer;
120 | if (requestUserId <= 0) {
121 | buffer = new DataBuffer();
122 | buffer.writeInt(requestUserId); // request uuid
123 | buffer.writeInt(0); // file count
124 | return MoguUtil.writeAttachments(buffer, attachment);
125 | }
126 |
127 | buffer = new DataBuffer();
128 | List fileList = FileModel.getInstance().getUserFiles(
129 | requestUserId); // 这里不会返回null
130 | int size = fileList.size();
131 | buffer.writeInt(requestUserId); // request userId
132 | buffer.writeInt(size);
133 | TransmitFile file = null;
134 | for (int i = 0; i < size; i++) {
135 | file = fileList.get(i);
136 | buffer.writeInt(file.getFromUserId());
137 | buffer.writeString(file.getTaskId());
138 | buffer.writeString(file.getFilePath());
139 | buffer.writeInt(file.getFileSize());
140 | }
141 | return MoguUtil.writeAttachments(buffer, attachment);
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/DepartAction.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.sql.SQLException;
4 | import java.util.Iterator;
5 | import java.util.Map;
6 |
7 | import com.mogujie.ares.data.Department;
8 | import com.mogujie.ares.extend.BaseAction;
9 | import com.mogujie.ares.lib.logger.Logger;
10 | import com.mogujie.ares.lib.logger.LoggerFactory;
11 | import com.mogujie.ares.lib.net.DataBuffer;
12 | import com.mogujie.ares.model.DepartModel;
13 | import com.mogujie.ares.util.MoguUtil;
14 |
15 | public class DepartAction extends BaseAction {
16 | private static final Logger logger = LoggerFactory
17 | .getLogger(DepartAction.class);
18 |
19 | /*
20 | *
21 | * @Description: 获取所有部门信息
22 | *
23 | * @param fromUserId
24 | *
25 | * @return
26 | */
27 | public DataBuffer getDepartmentInfo(int fromUserId, DataBuffer attachment,
28 | int version) {
29 | logger.info("get all department info by user : " + fromUserId);
30 | DataBuffer buffer;
31 | if (fromUserId <= 0) {
32 | buffer = new DataBuffer();
33 | buffer.writeInt(fromUserId);
34 | buffer.writeInt(0);
35 | buffer.writeInt(attachment.readableBytes());
36 | if (attachment.readableBytes() > 0) {
37 | buffer.writeDataBuffer(attachment);
38 | }
39 | return buffer;
40 | }
41 |
42 | try {
43 | Map departments = DepartModel.getInstance()
44 | .getDepartmentInfo();
45 | buffer = new DataBuffer();
46 | buffer.writeInt(fromUserId); // 发请求的用户
47 | buffer.writeInt(departments.size()); // 查询到的部门个数
48 | Department department;
49 | Iterator it = departments.keySet().iterator();
50 | while (it.hasNext()) {
51 | department = departments.get(it.next());
52 | buffer.writeInt(department.getDepartId()); // 部门id
53 | buffer.writeString(department.getTitle()); // 部门标题
54 | buffer.writeString(department.getDescription()); // 部门描述
55 | buffer.writeInt(department.getParentDepartId()); // 上级部门ID
56 | buffer.writeInt(department.getLeader()); // 部门leader
57 | buffer.writeInt(department.getStatus()); // 部门状态
58 | }
59 | } catch (SQLException e) {
60 | logger.error("get all department info error with reason : ", e);
61 | buffer = new DataBuffer();
62 | buffer.writeInt(fromUserId);
63 | buffer.writeInt(0);
64 | }
65 |
66 | return MoguUtil.writeAttachments(buffer, attachment);
67 | }
68 |
69 | // /**
70 | // *
71 | // * @Description: 获取组织架构信息(部门信息+员工)
72 | // * @param fromUserId
73 | // * @param departIds
74 | // * @return
75 | // */
76 | // public DataBuffer getOrganizationalChart(int fromUserId,
77 | // DataBuffer attachment, int version) {
78 | // logger.info("get organizational chart by user : " + fromUserId);
79 | // DataBuffer buffer;
80 | // if (fromUserId <= 0) {
81 | // buffer = new DataBuffer();
82 | // buffer.writeInt(fromUserId);
83 | // buffer.writeInt(0);
84 | // buffer.writeInt(attachment.readableBytes());
85 | // if (attachment.readableBytes() > 0) {
86 | // buffer.writeDataBuffer(attachment);
87 | // }
88 | // return buffer;
89 | // }
90 | //
91 | // try {
92 | // Map departments = DepartModel.getInstance()
93 | // .getDepartmentInfo();
94 | // buffer = new DataBuffer();
95 | // buffer.writeInt(fromUserId); // 发请求的用户
96 | // buffer.writeInt(departments.size()); // 查询到详细信息的用户数
97 | // Department department;
98 | // Iterator it = departments.keySet().iterator();
99 | // Set setUsers = null;
100 | // while (it.hasNext()) {
101 | // department = departments.get(it.next());
102 | // buffer.writeInt(department.getDepartId()); // 部门id
103 | // buffer.writeString(department.getTitle()); // 部门标题
104 | // buffer.writeString(department.getDescription()); // 部门描述
105 | // buffer.writeInt(department.getParentDepartId()); // 上级部门ID
106 | // buffer.writeString(department.getLeader()); // 部门leader
107 | // buffer.writeInt(department.getStatus()); // 部门状态
108 | //
109 | // setUsers = UserModel.getInstance().getUserInfoByDepartId(
110 | // department.getDepartId());
111 | // buffer.writeInt(setUsers.size()); // 部门人员数
112 | // Iterator itr = setUsers.iterator();
113 | // User user = null;
114 | // while (itr.hasNext()) {
115 | // user = itr.next();
116 | // buffer.writeInt(user.getUserId());
117 | // buffer.writeInt(user.getSex());
118 | // buffer.writeString(user.getUname());
119 | // buffer.writeString(user.getUnick());
120 | // buffer.writeString(user.getAvatar());
121 | // buffer.writeString(user.getTitle());
122 | // buffer.writeInt(user.getJobNumber());
123 | // buffer.writeString(user.getMail());
124 | // buffer.writeString(user.getTelphone());
125 | // }
126 | // }
127 | // } catch (SQLException e) {
128 | // logger.error("get organizational chart error with reason : ", e);
129 | // buffer = new DataBuffer();
130 | // buffer.writeInt(fromUserId);
131 | // buffer.writeInt(0);
132 | // }
133 | //
134 | // return MoguUtil.writeAttachments(buffer, attachment);
135 | // }
136 | }
137 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/lib/net/DataBuffer.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.lib.net;
2 |
3 | import java.util.List;
4 |
5 | import org.jboss.netty.buffer.ChannelBuffer;
6 | import org.jboss.netty.buffer.ChannelBuffers;
7 |
8 | /**
9 | *
10 | * @Description: 数据缓冲区对象,直接封装了netty的ChannelBuffer
11 | * @author ziye - ziye[at]mogujie.com
12 | * @date 2013-7-21 下午3:40:52
13 | *
14 | */
15 | public class DataBuffer {
16 |
17 | protected ChannelBuffer buffer;
18 |
19 | public DataBuffer() {
20 | buffer = ChannelBuffers.dynamicBuffer();
21 | }
22 |
23 | public DataBuffer(ChannelBuffer binaryBuffer) {
24 | buffer = binaryBuffer;
25 | }
26 |
27 | public DataBuffer(int length) {
28 | buffer = ChannelBuffers.buffer(length);
29 | }
30 |
31 | public byte[] array() {
32 | return buffer.array();
33 | }
34 |
35 | public void setOrignalBuffer(ChannelBuffer buffer) {
36 | this.buffer = buffer;
37 | }
38 |
39 | public ChannelBuffer getOrignalBuffer() {
40 | return buffer;
41 | }
42 |
43 | public void writeByte(int value) {
44 | buffer.writeByte(value);
45 | }
46 |
47 | public byte readByte() {
48 | return buffer.readByte();
49 | }
50 |
51 | public void writeBytes(byte[] bytes) {
52 | buffer.writeBytes(bytes);
53 | }
54 |
55 | public byte[] readBytes(int length) {
56 | byte[] bytes = new byte[length];
57 | buffer.readBytes(bytes);
58 | return bytes;
59 | }
60 |
61 | public int readInt() {
62 | if(buffer.readable()){
63 | return buffer.readInt();
64 | }else{
65 | return 0;
66 | }
67 | }
68 |
69 | public void writeInt(int value) {
70 | buffer.writeInt(value);
71 | }
72 |
73 | public char readChar() {
74 | return buffer.readChar();
75 | }
76 |
77 | public void writeChar(char c) {
78 | buffer.writeChar(c);
79 | }
80 |
81 | public long readLong() {
82 | return buffer.readLong();
83 | }
84 |
85 | public void writeLong(long value) {
86 | buffer.writeLong(value);
87 | }
88 |
89 | public double readDouble(){
90 | return buffer.readDouble();
91 | }
92 |
93 | public void writeDouble(double value){
94 | buffer.writeDouble(value);
95 | }
96 |
97 | /**
98 | * 读取一个字符串
99 | * @return
100 | * 格式:前导length表示字符串的byte数
101 | * length(4字节)string(length字节)
102 | */
103 | public String readString() {
104 | int length = readInt();
105 | byte[] bytes = readBytes(length);
106 |
107 | return new String(bytes);
108 | }
109 |
110 | /**
111 | * 写入一个字符串
112 | * @param str
113 | * 数据格式见方法readString()
114 | */
115 | public void writeString(String str) {
116 | byte[] bytes = str.getBytes();
117 | writeInt(bytes.length);
118 | writeBytes(bytes);
119 | }
120 |
121 | /**
122 | * 读取int数组
123 | * @return
124 | * 格式:前导count表示数组中有多少个元素
125 | * count(4字节)int1(4字节)...intCount(4字节)
126 | */
127 | public int[] readIntArray() {
128 | int count = readInt();
129 | int[] intArray = new int[count];
130 | for(int i = 0; i < count; i++) {
131 | intArray[i] = readInt();
132 | }
133 | return intArray;
134 | }
135 |
136 | /**
137 | * 写入int数组
138 | * @param intArray 格式见readIntArray()
139 | */
140 | public void writeIntArray(int[] intArray) {
141 | int count = intArray.length;
142 | writeInt(count);
143 | for(int i = 0; i < count; i++) {
144 | writeInt(intArray[i]);
145 | }
146 | }
147 |
148 | /**
149 | *
150 | * @Description: 写入一个int的list,list转数组太蛋疼了
151 | * @param intList
152 | */
153 | public void writeIntList(List intList) {
154 | if(intList == null || intList.isEmpty()) {
155 | writeInt(0);
156 | return ;
157 | }
158 | int count = intList.size();
159 | writeInt(count);
160 | for(int i = 0; i < count; i++) {
161 | writeInt(intList.get(i));
162 | }
163 | }
164 |
165 | /**
166 | * 读取byte数组
167 | * @return
168 | * 格式:前导count表示数组中有多少个元素
169 | * count(4字节)byte1(4字节)...byteCount(4字节)
170 | */
171 | public byte[] readByteArray() {
172 | int length = readInt(); // 获取长度
173 | byte[] bytes = new byte[length];
174 | buffer.readBytes(bytes);
175 | return bytes;
176 | }
177 |
178 | /**
179 | * 写入byte数组
180 | * @param byteArray 格式见readByteArray()
181 | */
182 | public void writeByteArray(byte[] byteArray) {
183 | int length = byteArray.length;
184 | writeInt(length);
185 | buffer.writeBytes(byteArray);
186 | }
187 | /**
188 | * 读取String数组
189 | * @return
190 | * 格式:前导count表示数组中有多少个元素
191 | * count(4字节)string1(4字节)...stringsCount(4字节)
192 | */
193 | public String[] readStringArray() {
194 | int count = readInt(); // 获取长度
195 | String[] strArray = new String[count];
196 | for(int i = 0; i < count; i++) {
197 | strArray[i] = readString();
198 | }
199 | return strArray;
200 | }
201 |
202 | /**
203 | * 写入String数组
204 | * @param byteArray 格式见readStringArray()
205 | */
206 | public void writeStringArray(String[] strArray) {
207 | int count = strArray.length;
208 | writeInt(count);
209 | for(int i = 0; i < count; i++) {
210 | writeString(strArray[i]);
211 | }
212 | }
213 |
214 | /**
215 | * 获取有效(可读取)的byte数
216 | * @return
217 | */
218 | public int readableBytes() {
219 | return buffer.readableBytes();
220 | }
221 |
222 | public DataBuffer readDataBuffer() {
223 | if(buffer == null || buffer.readableBytes() == 0) {
224 | return new DataBuffer(0);
225 | }
226 | int length = readInt();
227 | DataBuffer dataBuffer = new DataBuffer(0);
228 | dataBuffer.setOrignalBuffer(buffer.readBytes(length));
229 | return dataBuffer;
230 | }
231 |
232 | public void writeDataBuffer(DataBuffer inputBuffer) {
233 | if(inputBuffer == null || inputBuffer.readableBytes() == 0) {
234 | return ;
235 | }
236 | buffer.writeBytes(inputBuffer.buffer);
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/configure/Router.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.configure;
2 |
3 | import java.io.InputStream;
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 | import java.util.LinkedHashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import org.dom4j.DocumentException;
11 | import org.dom4j.Node;
12 | import org.dom4j.io.SAXReader;
13 |
14 | /**
15 | *
16 | * @ClassName: Router
17 | * @Description: Action的路由配置信息类
18 | * @author ziye - ziye(at)mogujie.com
19 | * @date 2013-7-20 下午5:36:56
20 | */
21 | public class Router {
22 |
23 | public static Router instance;
24 |
25 | // 路由的配置信息
26 | private Map actionMap = new HashMap();
27 |
28 | public static Router getInstance() {
29 | if(instance == null) {
30 | instance = new Router();
31 | }
32 |
33 | return instance;
34 | }
35 |
36 | private Router() {
37 |
38 | }
39 |
40 | public Map getActionMap() {
41 | return actionMap;
42 | }
43 |
44 | public void setActionMap(Map actionMap) {
45 | this.actionMap = actionMap;
46 | }
47 |
48 | /**
49 | *
50 | * @Title: getActionByRequestType
51 | * @Description: 根据头的type类型获得具体的action
52 | * @param @param type
53 | * @param @return
54 | * @return ActionItem
55 | * @throws
56 | */
57 | public ActionDescricptor getActionByRequestType(int type) {
58 | return actionMap.get(type);
59 | }
60 |
61 | /**
62 | *
63 | * @Title: load
64 | * @Description: 装在配置,配置文件格式如下:
65 | *
66 | *
67 | *
68 | * com.mogujie.ares.extend.action.Monitor
69 | * heartbeat
70 | * 1
71 | * 1
72 | *
73 | * ${channel.remoteAddress}
74 | *
75 | *
76 | *
77 | *
78 | *
79 | *
80 | *
81 | * @param @param file
82 | * @param @throws Exception
83 | * @return void
84 | * @throws
85 | */
86 | @SuppressWarnings({"unchecked"})
87 | public void load(String file) throws Exception {
88 | if(file == null || file.length() == 0) {
89 | throw new Exception("Route file is not specified!");
90 | }
91 |
92 | InputStream is = Router.class.getClassLoader().getResourceAsStream(file);
93 | if(is == null) {
94 | throw new Exception("Route file is not found! " + file);
95 | }
96 | SAXReader reader = new SAXReader();
97 | try {
98 | org.dom4j.Document configDocument = reader.read(is);
99 | List nodes = configDocument.selectNodes("//route/requests/request");
100 | Node node;
101 | ActionDescricptor item;
102 | for(int i = 0; i < nodes.size(); i++)
103 | {
104 | item = new ActionDescricptor();
105 |
106 | node = nodes.get(i);
107 | Node actionClassNode = node.selectSingleNode("action-class");
108 | item.setActionClass(actionClassNode.getStringValue());
109 | Node methodNode = node.selectSingleNode("method");
110 | item.setMethod(methodNode.getStringValue());
111 | Node requestTypeNode = node.selectSingleNode("request-type");
112 | item.setRequestType(Integer.parseInt(requestTypeNode.getStringValue()));
113 | Node responseTypeNode = node.selectSingleNode("response-type");
114 | item.setResponseType(Integer.parseInt(responseTypeNode.getStringValue()));
115 |
116 | // params
117 | Node paramsNode = node.selectSingleNode("params");
118 | List paramNodes = paramsNode.selectNodes("*"); // 取得所有的子节点
119 | Map params = new LinkedHashMap();
120 | Node param;
121 | for(int j = 0; j < paramNodes.size(); j++) {
122 | param = paramNodes.get(j);
123 | params.put(param.getStringValue(), param.getName());
124 | }
125 | item.setParams(params);
126 |
127 | // filters
128 | Node filter;
129 | Node filtersNode = node.selectSingleNode("filters");
130 | List filterNodes = filtersNode.selectNodes("*");
131 | List filters = new ArrayList();
132 | for(int j = 0; j < filterNodes.size(); j++) {
133 | filter = filterNodes.get(j);
134 | filters.add(filter.getStringValue());
135 | }
136 | String[] str = new String[filters.size()];
137 | filters.toArray(str);
138 | item.setFilters(str);
139 |
140 | actionMap.put(item.getRequestType(), item);
141 | }
142 | } catch (DocumentException e) {
143 | throw e;
144 | }
145 | }
146 |
147 | /**
148 | *
149 | * @ClassName: ActionItem
150 | * @Description: 单个Action的描述类
151 | * @author ziye - ziye(at)mogujie.com
152 | * @date 2013-7-20 下午5:42:09
153 | *
154 | */
155 | public class ActionDescricptor{
156 |
157 | // 类名
158 | private String actionClass;
159 |
160 | // 处理请求的方法名
161 | private String method;
162 |
163 | // 请求头的type,这种type的请求才处理
164 | private int requestType;
165 |
166 | // 响应的type
167 | private int responseType;
168 |
169 | // 请求的参数
170 | private Map params;
171 |
172 | // filter过滤
173 | private String[] filters;
174 |
175 | public ActionDescricptor() {
176 | }
177 |
178 | public String getActionClass() {
179 | return actionClass;
180 | }
181 |
182 | public void setActionClass(String actionClass) {
183 | this.actionClass = actionClass;
184 | }
185 |
186 | public String getMethod() {
187 | return method;
188 | }
189 |
190 | public void setMethod(String method) {
191 | this.method = method;
192 | }
193 |
194 | public int getRequestType() {
195 | return requestType;
196 | }
197 |
198 | public void setRequestType(int requestType) {
199 | this.requestType = requestType;
200 | }
201 |
202 | public Map getParams() {
203 | return params;
204 | }
205 |
206 | public void setParams(Map params) {
207 | this.params = params;
208 | }
209 |
210 | public String[] getFilters() {
211 | return filters;
212 | }
213 |
214 | public void setFilters(String[] filters) {
215 | this.filters = filters;
216 | }
217 |
218 | public int getResponseType() {
219 | return responseType;
220 | }
221 |
222 | public void setResponseType(int responseType) {
223 | this.responseType = responseType;
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/configure/PBRouter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.configure;
2 |
3 | import java.io.InputStream;
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 | import java.util.LinkedHashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import org.dom4j.DocumentException;
11 | import org.dom4j.Node;
12 | import org.dom4j.io.SAXReader;
13 |
14 | /**
15 | *
16 | * @ClassName: Router
17 | * @Description: Action的路由配置信息类
18 | * @author ziye - ziye(at)mogujie.com
19 | * @date 2013-7-20 下午5:36:56
20 | */
21 | public class PBRouter {
22 |
23 | public static PBRouter instance;
24 |
25 | // 路由的配置信息
26 | private Map actionMap = new HashMap();
27 |
28 | public static PBRouter getInstance() {
29 | if(instance == null) {
30 | instance = new PBRouter();
31 | }
32 |
33 | return instance;
34 | }
35 |
36 | private PBRouter() {
37 |
38 | }
39 |
40 | public Map getActionMap() {
41 | return actionMap;
42 | }
43 |
44 | public void setActionMap(Map actionMap) {
45 | this.actionMap = actionMap;
46 | }
47 |
48 | /**
49 | *
50 | * @Title: getActionByRequestType
51 | * @Description: 根据头的type类型获得具体的action
52 | * @param @param type
53 | * @param @return
54 | * @return ActionItem
55 | * @throws
56 | */
57 | public ActionDescricptor getActionByRequestType(int type) {
58 | return actionMap.get(type);
59 | }
60 |
61 | /**
62 | *
63 | * @Title: load
64 | * @Description: 装在配置,配置文件格式如下:
65 | *
66 | *
67 | *
68 | * com.mogujie.ares.extend.action.Monitor
69 | * heartbeat
70 | * 1
71 | * 1
72 | *
73 | * ${channel.remoteAddress}
74 | *
75 | *
76 | *
77 | *
78 | *
79 | *
80 | *
81 | * @param @param file
82 | * @param @throws Exception
83 | * @return void
84 | * @throws
85 | */
86 | @SuppressWarnings({"unchecked"})
87 | public void load(String file) throws Exception {
88 | if(file == null || file.length() == 0) {
89 | throw new Exception("Route file is not specified!");
90 | }
91 |
92 | InputStream is = PBRouter.class.getClassLoader().getResourceAsStream(file);
93 | if(is == null) {
94 | throw new Exception("Route file is not found! " + file);
95 | }
96 | SAXReader reader = new SAXReader();
97 | try {
98 | org.dom4j.Document configDocument = reader.read(is);
99 | List nodes = configDocument.selectNodes("//route/requests/request");
100 | Node node;
101 | ActionDescricptor item;
102 | for(int i = 0; i < nodes.size(); i++)
103 | {
104 | item = new ActionDescricptor();
105 |
106 | node = nodes.get(i);
107 | Node actionClassNode = node.selectSingleNode("action-class");
108 | item.setActionClass(actionClassNode.getStringValue());
109 | Node methodNode = node.selectSingleNode("method");
110 | item.setMethod(methodNode.getStringValue());
111 | Node requestTypeNode = node.selectSingleNode("request-type");
112 | item.setRequestType(Integer.parseInt(requestTypeNode.getStringValue()));
113 | Node responseTypeNode = node.selectSingleNode("response-type");
114 | item.setResponseType(Integer.parseInt(responseTypeNode.getStringValue()));
115 |
116 | // params
117 | Node paramsNode = node.selectSingleNode("params");
118 | List paramNodes = paramsNode.selectNodes("*"); // 取得所有的子节点
119 | Map params = new LinkedHashMap();
120 | Node param;
121 | for(int j = 0; j < paramNodes.size(); j++) {
122 | param = paramNodes.get(j);
123 | params.put(param.getStringValue(), param.getName());
124 | }
125 | item.setParams(params);
126 |
127 | // filters
128 | Node filter;
129 | Node filtersNode = node.selectSingleNode("filters");
130 | List filterNodes = filtersNode.selectNodes("*");
131 | List filters = new ArrayList();
132 | for(int j = 0; j < filterNodes.size(); j++) {
133 | filter = filterNodes.get(j);
134 | filters.add(filter.getStringValue());
135 | }
136 | String[] str = new String[filters.size()];
137 | filters.toArray(str);
138 | item.setFilters(str);
139 |
140 | actionMap.put(item.getRequestType(), item);
141 | }
142 | } catch (DocumentException e) {
143 | throw e;
144 | }
145 | }
146 |
147 | /**
148 | *
149 | * @ClassName: ActionItem
150 | * @Description: 单个Action的描述类
151 | * @author ziye - ziye(at)mogujie.com
152 | * @date 2013-7-20 下午5:42:09
153 | *
154 | */
155 | public class ActionDescricptor{
156 |
157 | // 类名
158 | private String actionClass;
159 |
160 | // 处理请求的方法名
161 | private String method;
162 |
163 | // 请求头的type,这种type的请求才处理
164 | private int requestType;
165 |
166 | // 响应的type
167 | private int responseType;
168 |
169 | // 请求的参数
170 | private Map params;
171 |
172 | // filter过滤
173 | private String[] filters;
174 |
175 | public ActionDescricptor() {
176 | }
177 |
178 | public String getActionClass() {
179 | return actionClass;
180 | }
181 |
182 | public void setActionClass(String actionClass) {
183 | this.actionClass = actionClass;
184 | }
185 |
186 | public String getMethod() {
187 | return method;
188 | }
189 |
190 | public void setMethod(String method) {
191 | this.method = method;
192 | }
193 |
194 | public int getRequestType() {
195 | return requestType;
196 | }
197 |
198 | public void setRequestType(int requestType) {
199 | this.requestType = requestType;
200 | }
201 |
202 | public Map getParams() {
203 | return params;
204 | }
205 |
206 | public void setParams(Map params) {
207 | this.params = params;
208 | }
209 |
210 | public String[] getFilters() {
211 | return filters;
212 | }
213 |
214 | public void setFilters(String[] filters) {
215 | this.filters = filters;
216 | }
217 |
218 | public int getResponseType() {
219 | return responseType;
220 | }
221 |
222 | public void setResponseType(int responseType) {
223 | this.responseType = responseType;
224 | }
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/UserAction.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.sql.SQLException;
4 | import java.util.Arrays;
5 | import java.util.Iterator;
6 | import java.util.Map;
7 | import java.util.Set;
8 |
9 | import com.mogujie.ares.data.User;
10 | import com.mogujie.ares.extend.BaseAction;
11 | import com.mogujie.ares.lib.logger.Logger;
12 | import com.mogujie.ares.lib.logger.LoggerFactory;
13 | import com.mogujie.ares.lib.net.DataBuffer;
14 | import com.mogujie.ares.model.UserModel;
15 | import com.mogujie.ares.util.MoguUtil;
16 |
17 | /**
18 | *
19 | * @Description: 用户相关的类
20 | * @author ziye - ziye[at]mogujie.com
21 | * @date 2013-8-12 下午4:55:45
22 | *
23 | */
24 | public class UserAction extends BaseAction {
25 |
26 | private static final Logger logger = LoggerFactory
27 | .getLogger(UserAction.class);
28 |
29 | /**
30 | *
31 | * @Description: 获取用户信息
32 | * @param fromUserId
33 | * @param userIds
34 | * @return
35 | */
36 | public DataBuffer getUsersInfo(int fromUserId, int[] userIds,
37 | DataBuffer attachment, int version) {
38 | logger.info("get users info : " + Arrays.toString(userIds));
39 | DataBuffer buffer;
40 | if (userIds == null || userIds.length <= 0) {
41 | buffer = new DataBuffer();
42 | buffer.writeInt(fromUserId);
43 | buffer.writeInt(0);
44 | return MoguUtil.writeAttachments(buffer, attachment);
45 | }
46 |
47 | try {
48 | Map users = UserModel.getInstance().getUserInfo(
49 | userIds);
50 | buffer = new DataBuffer();
51 | buffer.writeInt(fromUserId); // 发请求的用户
52 | buffer.writeInt(users.size()); // 查询到详细信息的用户数
53 | User user;
54 | Iterator it = users.keySet().iterator();
55 | String logText = "response userInfo: ";
56 | while (it.hasNext()) {
57 | user = users.get(it.next());
58 | buffer.writeInt(user.getUserId()); // 用户ID
59 | buffer.writeString(user.getUname()); // 用户名
60 | buffer.writeString(user.getUnick()); // 用户昵称
61 | buffer.writeString(user.getAvatar()); // 用户头像
62 | buffer.writeString(user.getTitle()); // 用户职称
63 | buffer.writeString(user.getPosition()); // 用户地址
64 | buffer.writeInt(user.getStatus()); // 用户在职等状态
65 | buffer.writeInt(user.getSex()); // 用户性别
66 | buffer.writeInt(user.getDepartId()); // 用户所在部门ID
67 | buffer.writeInt(user.getJobNumber()); // 用户工号
68 | buffer.writeString(user.getTelphone()); // 用户电话
69 | buffer.writeString(user.getMail()); // 用户邮箱
70 | logText += "userId=" + user.getUserId() + ", ";
71 | }
72 | logger.info(logText);
73 | } catch (SQLException e) {
74 | logger.error("get users info error with reason : ", e);
75 | buffer = new DataBuffer();
76 | buffer.writeInt(fromUserId);
77 | buffer.writeInt(0);
78 | }
79 |
80 | return MoguUtil.writeAttachments(buffer, attachment);
81 | }
82 |
83 | /**
84 | *
85 | * @Description: 获取所有用户信息
86 | * @param fromUserId
87 | * @return
88 | */
89 | public DataBuffer getAllUsersInfo(int fromUserId, DataBuffer attachment,
90 | int version) {
91 | logger.info("get all users info by : " + fromUserId);
92 | DataBuffer buffer;
93 | if (fromUserId <= 0) {
94 | buffer = new DataBuffer();
95 | buffer.writeInt(fromUserId);
96 | buffer.writeInt(0);
97 | buffer.writeInt(attachment.readableBytes());
98 | if (attachment.readableBytes() > 0) {
99 | buffer.writeDataBuffer(attachment);
100 | }
101 | return buffer;
102 | }
103 |
104 | try {
105 | Set users = UserModel.getInstance().getAllUserInfo();
106 | buffer = new DataBuffer();
107 | buffer.writeInt(fromUserId); // 发请求的用户
108 | buffer.writeInt(users.size()); // 查询到详细信息的用户数
109 | User user;
110 | Iterator it = users.iterator();
111 | while (it.hasNext()) {
112 | user = it.next();
113 | buffer.writeInt(user.getUserId()); // 用户ID
114 | buffer.writeString(user.getUname()); // 用户名
115 | buffer.writeString(user.getUnick()); // 用户昵称
116 | buffer.writeString(user.getAvatar()); // 用户头像
117 | buffer.writeString(user.getTitle()); // 用户职称
118 | buffer.writeString(user.getPosition()); // 用户地址
119 | buffer.writeInt(user.getStatus()); // 用户在职等状态
120 | buffer.writeInt(user.getSex()); // 用户性别
121 | buffer.writeInt(user.getDepartId()); // 用户所在部门ID
122 | buffer.writeInt(user.getJobNumber()); // 用户工号
123 | buffer.writeString(user.getTelphone()); // 用户电话
124 | buffer.writeString(user.getMail()); // 用户邮箱
125 | }
126 | } catch (SQLException e) {
127 | logger.error("get all user info error with reason", e);
128 | buffer = new DataBuffer();
129 | buffer.writeInt(fromUserId);
130 | buffer.writeInt(0);
131 | }
132 |
133 | return MoguUtil.writeAttachments(buffer, attachment);
134 | }
135 |
136 | // /*
137 | // * @Description: 修改用户头像
138 | // * @param userId
139 | // * 用户Id
140 | // * @param avatar
141 | // * 用户头像url
142 | // * @return
143 | // */
144 | // public DataBuffer alterAvatar(int userId, String avatar,
145 | // DataBuffer attachment, int version) {
146 | // // logger.info("login: " + userId + ", new avatar: " + avatar);
147 | // int resultCode = 0;
148 | // // 数据校验扔到model层,这里不需要判断
149 | // try {
150 | // resultCode = UserModel.getInstance().alterAvatar(userId, avatar);
151 | // } catch (Exception e) {
152 | // resultCode = 1;
153 | // logger.error("error: update user's avatar failed! userId: "
154 | // + userId + " avatar: " + avatar + e.toString());
155 | // }
156 | // DataBuffer buffer = new DataBuffer();
157 | // buffer.writeInt(userId);
158 | // buffer.writeInt(resultCode);
159 | //
160 | // return MoguUtil.writeAttachments(buffer, attachment);
161 | // }
162 |
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/TimerManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.concurrent.ConcurrentHashMap;
9 | import java.util.concurrent.ExecutionException;
10 | import java.util.concurrent.ScheduledFuture;
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import com.mogujie.ares.configure.SysConstants;
14 | import com.mogujie.ares.lib.logger.Logger;
15 | import com.mogujie.ares.lib.logger.LoggerFactory;
16 | import com.mogujie.ares.timer.Timer;
17 |
18 | /**
19 | *
20 | * @Description: 定时脚本管理
21 | * @author ziye - ziye[at]mogujie.com
22 | * @date 2013-7-22 下午1:03:05
23 | *
24 | * @param
25 | */
26 | public class TimerManager
27 | {
28 | private static final Logger logger = LoggerFactory.getLogger(TimerManager.class);
29 |
30 | ConcurrentHashMap> timerConfigure = new ConcurrentHashMap>();
31 |
32 | private List tasks = new ArrayList();
33 |
34 | ConcurrentHashMap> taskFutures = new ConcurrentHashMap>();
35 |
36 | private boolean isLaunch = false;
37 |
38 | private Timer timer; // 一个线程池
39 |
40 | private static TimerManager timerManagerInstance;
41 |
42 | public static TimerManager getInstance()
43 | {
44 | if(timerManagerInstance == null)
45 | {
46 | timerManagerInstance = new TimerManager();
47 | }
48 | return timerManagerInstance;
49 | }
50 |
51 | private TimerManager() {
52 | initialize();
53 | }
54 |
55 | /**
56 | *
57 | * @Description: 初始化
58 | */
59 | private void initialize()
60 | {
61 | timerConfigure = ConfigureManager.getInstance().getTimerConfig();
62 |
63 | timer = Timer.getInstance();
64 | }
65 |
66 | /**
67 | *
68 | * @Description: 创建所有初始化的任务
69 | */
70 | private void createInitializeAllTasks()
71 | {
72 | Iterator iter = timerConfigure.keySet().iterator();
73 | while(iter.hasNext())
74 | {
75 | String taskName = iter.next();
76 | try {
77 | Class> taskClass;
78 | taskClass = Class.forName(taskName);
79 | Object taskInstance = taskClass.newInstance();
80 | if(taskInstance != null) {
81 | Runnable taskRunnable = (Runnable) taskInstance;
82 | tasks.add(taskRunnable);
83 | }
84 | } catch (ClassNotFoundException e) {
85 | logger.error("", e);
86 | } catch (InstantiationException e) {
87 | logger.error("", e);
88 | } catch (IllegalAccessException e) {
89 | logger.error("", e);
90 | }
91 | }
92 | }
93 |
94 | /**
95 | *
96 | * @throws Exception
97 | * @Description: 提交一个初始化的任务
98 | */
99 | protected void submitInitialTasks() throws Exception
100 | {
101 | if(tasks.size() > 0)
102 | {
103 | Iterator iter = tasks.iterator();
104 | while(iter.hasNext())
105 | {
106 | try{
107 | Runnable initialTask = iter.next();
108 | HashMap taskConfig = timerConfigure.get(initialTask.getClass().getName());
109 | if( ! taskConfig.get("enable").equals(SysConstants.TASK_ENABLE_RUN)) {
110 | continue;
111 | }
112 |
113 | String taskType = taskConfig.get("taskType");
114 | if(taskType.equals("rateTask")) {
115 | submitFixedRateTask(initialTask);
116 | } else if(taskType.equals("delayTask")) {
117 | submitFixedDelayTask(initialTask);
118 | } else if(taskType.equals("oneShot")) {
119 | submitOneShotTask(initialTask);
120 | }
121 | } catch (Exception e) {
122 | throw e;
123 | }
124 | }
125 | }
126 | }
127 |
128 | /**
129 | *
130 | * @Description: 初始化加载
131 | * @throws Exception
132 | */
133 | public void lanuch() throws Exception
134 | {
135 | if( ! isLaunch)
136 | {
137 | createInitializeAllTasks();
138 |
139 | submitInitialTasks();
140 | }
141 | }
142 |
143 | /**
144 | * 提交定时类task
145 | */
146 | public void submitFixedRateTask(Runnable task)
147 | {
148 | logger.info("submit fixed rate task");
149 | String taskName = task.getClass().getName();
150 | long initialDelay = Integer.valueOf(timerConfigure.get(taskName).get("initdelay"));
151 | long period = Integer.valueOf(timerConfigure.get(taskName).get("periodordelay"));
152 | taskFutures.put(taskName, timer.submitFixedRateTask(task, initialDelay, period, TimeUnit.SECONDS));
153 | }
154 |
155 | /**
156 | * 提交delay循环执行的task
157 | */
158 | public void submitFixedDelayTask(Runnable task)
159 | {
160 | String taskName = task.getClass().getName();
161 | long initialDelay = Integer.valueOf(timerConfigure.get(taskName).get("initialdelay"));
162 | long delay = Integer.valueOf(timerConfigure.get(taskName).get("periodordelay"));
163 | taskFutures.put(taskName, timer.submitFixedDelayTask(task, initialDelay, delay, TimeUnit.SECONDS));
164 | }
165 |
166 | /**
167 | * 提交仅执行一次的task
168 | */
169 | public void submitOneShotTask(Runnable task)
170 | {
171 | String taskName = task.getClass().getName();
172 | long initialDelay = Integer.valueOf(timerConfigure.get(taskName).get("initaildelay"));
173 | taskFutures.put(taskName, timer.submitOneShotTask(task, initialDelay, TimeUnit.SECONDS));
174 | }
175 |
176 | /**
177 | *
178 | * @Description: 关闭定时任务
179 | */
180 | public void shutDown()
181 | {
182 | if(timer != null)
183 | {
184 | timer.shutDown();
185 |
186 | clean();
187 | }
188 | }
189 |
190 | /**
191 | *
192 | * @Description: 关闭定时任务,不等待当前自然结束
193 | */
194 | public void shutDownNow()
195 | {
196 | if(timer != null)
197 | {
198 | timer.shutDownNow();
199 |
200 | clean();
201 | }
202 | }
203 |
204 | /**
205 | *
206 | * @Description: 擦屁股
207 | */
208 | private void clean()
209 | {
210 | if(taskFutures.size() > 0)
211 | {
212 | taskFutures.clear();
213 | }
214 |
215 | if(tasks.size() > 0)
216 | {
217 | tasks.clear();
218 | }
219 | }
220 |
221 | /**
222 | * 检查task是否都完成了,如果没有,则堵塞调用的线程
223 | */
224 | public void checkTasksCompleted()
225 | {
226 | if(taskFutures.size() > 0)
227 | {
228 | Iterator>> iter = taskFutures.entrySet().iterator();
229 | while(iter.hasNext())
230 | {
231 | Map.Entry> entry = iter.next();
232 | try {
233 | entry.getValue().get(); // 这里会wait
234 | } catch (InterruptedException e) {
235 | logger.error("", e);
236 | } catch (ExecutionException e) {
237 | logger.error("", e);
238 | }
239 | }
240 | }
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.mogujie
6 | mogutalk-business
7 | 0.0.1-SNAPSHOT
8 | jar
9 |
10 | mogutalk-business
11 | http://maven.apache.org
12 |
13 |
14 |
15 |
16 | UTF-8
17 | 1.6
18 | STDOUT
19 | db-dev.properties
20 | cache-dev.properties
21 |
22 |
23 |
24 |
25 | product
26 |
27 |
28 | db-online.properties
29 | cache-online.properties
30 |
31 |
32 |
33 |
34 |
35 |
36 | dom4j
37 | dom4j
38 | 1.6
39 |
40 |
41 | jaxen
42 | jaxen
43 | 1.1.4
44 |
45 |
46 | mysql
47 | mysql-connector-java
48 | 5.1.25
49 |
50 |
51 | com.google.code.maven-play-plugin.org.allcolor.shanidom
52 | jaxen
53 | 1.1.1-patched-shani-1.4.17
54 |
55 |
56 | dom4j
57 | dom4j
58 | 1.6.1
59 |
60 |
61 | com.google.guava
62 | guava
63 | 14.0.1
64 |
65 |
66 | com.alibaba
67 | druid
68 | 0.2.9
69 |
70 |
71 | redis.clients
72 | jedis
73 | 2.0.0
74 | jar
75 | compile
76 |
77 |
78 | io.netty
79 | netty
80 | 3.6.6.Final
81 |
82 |
83 |
84 | org.slf4j
85 | jcl-over-slf4j
86 | 1.7.2
87 |
88 |
89 | ch.qos.logback
90 | logback-core
91 | 0.9.29
92 |
93 |
94 | ch.qos.logback
95 | logback-classic
96 | 0.9.29
97 |
98 |
99 | slf4j-api
100 | org.slf4j
101 |
102 |
103 | ch.qos.logback
104 | logback-core
105 |
106 |
107 |
108 |
109 | org.apache.httpcomponents
110 | httpclient
111 | 4.3.3
112 |
113 |
114 | commons-httpclient
115 | commons-httpclient
116 | 3.1
117 |
118 |
119 | commons-util
120 | commons-util
121 | final
122 |
123 |
124 | commons-lang
125 | commons-lang
126 | 2.6
127 |
128 |
129 | com.fasterxml.jackson.core
130 | jackson-core
131 | 2.3.2
132 |
133 |
134 | org.codehaus.jackson
135 | jackson-core-lgpl
136 | 1.9.13
137 |
138 |
139 | org.codehaus.jackson
140 | jackson-core-asl
141 | 1.9.13
142 |
143 |
144 | org.codehaus.jackson
145 | jackson-mapper-asl
146 | 1.9.13
147 |
148 |
149 |
150 |
151 | junit
152 | junit
153 | 4.8.1
154 | test
155 |
156 |
157 |
158 |
159 |
160 | org.apache.maven.plugins
161 | maven-surefire-plugin
162 | 2.4.2
163 |
164 | true
165 |
166 |
167 |
168 | maven-assembly-plugin
169 |
170 | false
171 |
172 | jar-with-dependencies
173 |
174 |
175 |
176 | com.mogujie.ares.MainServer
177 |
178 |
179 |
180 |
181 |
182 | make-assembly
183 | package
184 |
185 | assembly
186 |
187 |
188 |
189 |
190 |
191 | org.apache.maven.plugins
192 | maven-compiler-plugin
193 | 2.3.2
194 |
195 | ${version.jdk}
196 | ${version.jdk}
197 | ${project.build.sourceEncoding}
198 |
199 |
200 |
201 | org.apache.maven.plugins
202 | maven-resources-plugin
203 |
204 | ${project.build.sourceEncoding}
205 |
206 |
207 |
208 |
209 |
210 | src/main/resources
211 | true
212 |
213 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/Friendship.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.sql.SQLException;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 |
7 | import com.mogujie.ares.configure.BizConstants;
8 | import com.mogujie.ares.data.Group;
9 | import com.mogujie.ares.data.Relationship;
10 | import com.mogujie.ares.data.User;
11 | import com.mogujie.ares.extend.BaseAction;
12 | import com.mogujie.ares.lib.logger.Logger;
13 | import com.mogujie.ares.lib.logger.LoggerFactory;
14 | import com.mogujie.ares.lib.net.DataBuffer;
15 | import com.mogujie.ares.model.GroupModel;
16 | import com.mogujie.ares.model.RelationshipModel;
17 | import com.mogujie.ares.model.UserModel;
18 | import com.mogujie.ares.util.MoguUtil;
19 |
20 | /*
21 | * @Description: 好友相关的请求
22 | * @author ziye - ziye[at]mogujie.com
23 | * @date 2013-7-21 下午1:27:09
24 | */
25 | public class Friendship extends BaseAction {
26 |
27 | private static final Logger logger = LoggerFactory
28 | .getLogger(Friendship.class);
29 | private static User serviceUser; // 服务号小T
30 | private static Map mapFilterUserIds = new HashMap();// 要过滤的用户id
31 |
32 | public Friendship() {
33 | try {
34 | serviceUser = UserModel.getInstance().getServerUserInfo();
35 | if (null == serviceUser) {
36 | throw new IllegalArgumentException(
37 | "can't initialize server user info");
38 | }
39 | mapFilterUserIds.put(serviceUser.getUserId(), 1);
40 | } catch (SQLException e) {
41 | logger.error("initialize server user info error with reason : ", e);
42 | }
43 | }
44 |
45 | /*
46 | *
47 | * @Description: 获取用户的最近联系人
48 | *
49 | * @param userId 用户id
50 | *
51 | * @return
52 | */
53 | public DataBuffer getUserRecentContact(int userId, DataBuffer attachment,
54 | int version) {
55 | DataBuffer buffer = null;
56 |
57 | if (userId <= 0) {
58 | buffer = new DataBuffer();
59 | buffer.writeInt(userId);
60 | buffer.writeInt(0);
61 | return MoguUtil.writeAttachments(buffer, attachment);
62 | }
63 |
64 | DataBuffer tmpBuffer = new DataBuffer();
65 | buffer = new DataBuffer();
66 | buffer.writeInt(userId);
67 | int count = 0;
68 | StringBuffer logBuffer = new StringBuffer();
69 |
70 | try {
71 | logger.info("get recent contact list by " + userId);
72 | // 取好友列表
73 | Relationship[] friendshipArray = RelationshipModel.getInstance()
74 | .getRecentContactByUserId(userId, 100);
75 |
76 | if (serviceUser != null) {
77 | mapFilterUserIds.put(serviceUser.getUserId(), 1);
78 | tmpBuffer.writeInt(serviceUser.getUserId());
79 | tmpBuffer.writeInt(1767200461); // 2026-01-01 服务号默认置顶
80 | count++;
81 | }
82 | int fuid;
83 | if (friendshipArray != null && friendshipArray.length > 0) {
84 | for (int i = 0; i < friendshipArray.length; i++) {
85 | fuid = friendshipArray[i].getFriendUserId();
86 | if (mapFilterUserIds.containsKey(fuid)) {
87 | continue;
88 | }
89 | tmpBuffer.writeInt(fuid);
90 | tmpBuffer.writeInt(friendshipArray[i].getUpdated());
91 | logBuffer.append(", " + fuid + "-updateTime = " + friendshipArray[i].getUpdated() ); // log
92 | count++;
93 | }
94 | }
95 | } catch (SQLException e) {
96 | logger.error("get recent contact list error with reason : ", e);
97 | }
98 | logger.info("get recent contact list: userId=" + userId + ", friends:"
99 | + logBuffer.toString());
100 | if (tmpBuffer != null && tmpBuffer.readableBytes() > 0) {
101 | buffer.writeInt(count);
102 | buffer.writeDataBuffer(tmpBuffer);
103 | } else {
104 | buffer.writeInt(0);
105 | }
106 |
107 | return MoguUtil.writeAttachments(buffer, attachment);
108 | }
109 |
110 | /*
111 | *
112 | * @Description: 删除用户的最近联系人或群
113 | *
114 | * @param userId 用户id
115 | *
116 | * @param friendUserId 最近联系ID
117 | *
118 | * @param friendUserType 最近联系ID类型:1用户;2群
119 | *
120 | * @return
121 | */
122 | public DataBuffer deleteUserRecentContact(int userId, int friendUserId,
123 | int friendUserType, DataBuffer attachment, int version) {
124 | DataBuffer buffer = null;
125 |
126 | if (userId <= 0 || friendUserId <= 0 || friendUserType <= 0
127 | || BizConstants.SYS_SERVER_USER_ID == friendUserId) {
128 | buffer = new DataBuffer();
129 | buffer.writeInt(userId);
130 | buffer.writeInt(0);
131 | buffer.writeInt(friendUserId);
132 | buffer.writeInt(friendUserType);
133 | return MoguUtil.writeAttachments(buffer, attachment);
134 | }
135 |
136 | boolean isSuccess = false;
137 | try {
138 | if (1 == friendUserType) {
139 | isSuccess = RelationshipModel.getInstance()
140 | .deleteRecentContactByUserId(userId, friendUserId);
141 | } else {
142 | Group group = GroupModel.getInstance().getGroupInfo(
143 | friendUserId);
144 | // 如果群不存在则删除失败,群主也能将自己建的群从最近联系群中删除
145 | if (group == null || friendUserId != group.getGroupId()) {
146 | isSuccess = false;
147 | } else {
148 | isSuccess = GroupModel.getInstance()
149 | .deleteRecentContactByGroupId(userId, friendUserId,
150 | group);
151 | }
152 | }
153 | } catch (SQLException e) {
154 | logger.error("delete recent contact error: userId = " + userId
155 | + ", friendUserId = " + friendUserId + ", friendUserType ="
156 | + friendUserType, e);
157 | }
158 |
159 | buffer = new DataBuffer();
160 | buffer.writeInt(userId); // 用户Id
161 | if (isSuccess) { // 成功
162 | logger.info("delete recent contact success: result=" + isSuccess
163 | + ", userId=" + userId + ", friendUserId = " + friendUserId
164 | + ", friendUserType = " + friendUserType);
165 | buffer.writeInt(0); // 结果
166 | buffer.writeInt(friendUserId);
167 | buffer.writeInt(friendUserType);
168 | } else { // 失败
169 | logger.info("delete recent contact error: result=" + isSuccess
170 | + ", userId=" + userId + ", friendUserId = " + friendUserId
171 | + ", friendUserType = " + friendUserType);
172 | buffer.writeInt(1); // 结果
173 | buffer.writeInt(friendUserId);
174 | buffer.writeInt(friendUserType);
175 | }
176 |
177 | return MoguUtil.writeAttachments(buffer, attachment);
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/manager/ElegantStopManager.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.manager;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.File;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileReader;
7 | import java.io.FileWriter;
8 | import java.io.IOException;
9 | import java.util.Iterator;
10 | import java.util.Map;
11 | import java.util.concurrent.ConcurrentHashMap;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | import org.jboss.netty.channel.ChannelHandlerContext;
16 |
17 | import com.mogujie.ares.configure.BizConstants;
18 | import com.mogujie.ares.configure.SysConstants;
19 | import com.mogujie.ares.lib.logger.Logger;
20 | import com.mogujie.ares.lib.logger.LoggerFactory;
21 | import com.mogujie.ares.lib.net.DataBuffer;
22 | import com.mogujie.ares.lib.net.Packet;
23 |
24 | /**
25 | *
26 | * @Description: 传说中石头的优雅退出,单例
27 | * @author shitou - shitou[at]mogujie.com
28 | * @date 2013-7-22 上午11:15:53
29 | *
30 | */
31 | public class ElegantStopManager
32 | {
33 | private int port = 0;
34 |
35 | public static final Logger logger = LoggerFactory.getLogger(ElegantStopManager.class);
36 |
37 | private static ElegantStopManager instance = null;
38 |
39 | public static ElegantStopManager getInstance(int port)
40 | {
41 | if(instance == null)
42 | {
43 | instance = new ElegantStopManager();
44 | instance.port = port;
45 | instance.initialize();
46 | }
47 | return instance;
48 | }
49 |
50 | public void initialize()
51 | {
52 | resetStopConfigFileContent();
53 | generateShutDownShellFile();
54 | }
55 |
56 | /**
57 | * 启动的时候,重置文件内容
58 | */
59 | public void resetStopConfigFileContent()
60 | {
61 | File stopConfigFile = new File(getShutDownFilePath());
62 | if(stopConfigFile.exists())
63 | {
64 | stopConfigFile.delete();
65 | }
66 | }
67 |
68 | /**
69 | * 判断是否停止正在运行的实例
70 | * @return
71 | */
72 | public boolean isStopCurrentJavaInstance()
73 | {
74 | boolean isStop = false;
75 | File stopConfigFile = new File(getShutDownFilePath());
76 | // 文件不存在则新建个文件
77 | if( ! stopConfigFile.exists())
78 | {
79 | try {
80 | stopConfigFile.createNewFile();
81 | } catch (IOException e) {
82 | logger.error("", e);
83 | }
84 | return isStop;
85 | }
86 |
87 | BufferedReader bufferedReader = null;
88 | try {
89 | bufferedReader = new BufferedReader(new FileReader(stopConfigFile));
90 | String stopFileContent = bufferedReader.readLine();
91 | isStop = SysConstants.ELEGANT_STOP_CONTENT.equals(stopFileContent);
92 | } catch (FileNotFoundException e) {
93 | logger.error("", e);
94 | } catch (IOException e) {
95 | logger.error("", e);
96 | } finally {
97 | try {
98 | if(bufferedReader != null) {
99 | bufferedReader.close();
100 | }
101 | } catch (IOException e) {
102 | logger.error("", e);
103 | }
104 | }
105 | return isStop;
106 | }
107 |
108 | /**
109 | * @Description: 包装一个停止的数据包, type=200
110 | * @return
111 | */
112 | protected Packet generateStopReceivePacket()
113 | {
114 | Packet stopPacket = new Packet();
115 | stopPacket.setLength(SysConstants.PROTOCOL_HEADER_LENGTH + 4);
116 | stopPacket.setVersion(SysConstants.PROTOCOL_PREVIOUS_VERSION);
117 | stopPacket.setFlag((byte)0);
118 | stopPacket.setServiceId(BizConstants.DEFAULT_SERVICEID);
119 | stopPacket.setCommandId(BizConstants.COMMANDID_STOP_RECEIVE);
120 | stopPacket.setError((char)0);
121 | stopPacket.setReserved((char)0);
122 | DataBuffer dataBuffer = new DataBuffer(4); // 数据部分
123 | dataBuffer.writeInt(1);
124 | stopPacket.setContentBuffer(dataBuffer);
125 | return stopPacket;
126 | }
127 |
128 | /**
129 | *
130 | * @Description: netty停止接收数据
131 | * 向每个客户端都发一个即将关闭的数据包,告诉她们:“我要挂了~”
132 | */
133 | public void nettyStopReceivePacket()
134 | {
135 | ConcurrentHashMap nettyClients = NetworkManager.getInstance().getClientMap();
136 | if(nettyClients.size() > 0)
137 | {
138 | Iterator> iter = nettyClients.entrySet().iterator();
139 | while(iter.hasNext())
140 | {
141 | Map.Entry entry = iter.next();
142 |
143 | ChannelHandlerContext nettyClient = entry.getValue();
144 | nettyClient.getChannel().write(generateStopReceivePacket());
145 | }
146 | }
147 |
148 | }
149 |
150 | /**
151 | *
152 | * @Description: 退出系统
153 | */
154 | public void shutdown() {
155 |
156 | //告诉MsgServer,停止发送新的数据,并断开连接
157 | nettyStopReceivePacket();
158 |
159 | //断开netty
160 | NetworkManager networkManager = NetworkManager.getInstance();
161 | if(networkManager != null) {
162 |
163 | //检查是否还有work thread,如果有,sleep
164 | ExecutorService executor = NetworkManager.getInstance().getNettyWorkerTheadPool();
165 | //可能需要先暂停,再调用awaitTermination
166 | executor.shutdown();
167 | try {
168 | if( executor != null && !executor.awaitTermination(10, TimeUnit.SECONDS))
169 | {
170 | executor.shutdownNow();
171 | logger.error("Unable to completed all netty works");
172 | }
173 | } catch (InterruptedException e) {
174 | executor.shutdownNow();
175 | logger.error("", e);
176 | }
177 |
178 | networkManager.shutdown();
179 | }
180 |
181 | //关闭Timer
182 | TimerManager timerManager = TimerManager.getInstance();
183 | if(timerManager != null) {
184 |
185 | timerManager.shutDown();
186 |
187 | //检查timer是否关闭完成了,如果没有,则sleep
188 | timerManager.checkTasksCompleted();
189 |
190 | }
191 |
192 | //关闭DB
193 | DBManager dbManager = DBManager.getInstance();
194 | if(dbManager != null) {
195 | DBManager.getInstance().shutDown();
196 | }
197 | //关闭cache
198 | CacheManager cacheManager = CacheManager.getInstance();
199 | if(cacheManager != null) {
200 | CacheManager.getInstance().shutDown();
201 | }
202 | }
203 |
204 | /**
205 | * 获取shutdown的文件路径
206 | * @return
207 | */
208 | public String getShutDownFilePath()
209 | {
210 | String filePath = null;
211 | if(port > 0)
212 | {
213 | filePath = SysConstants.ELEGANT_STOP_FILE + port;
214 | }
215 | return filePath;
216 | }
217 |
218 | /**
219 | * 生成shutdown文件
220 | */
221 | public void generateShutDownShellFile()
222 | {
223 | if(port > 0)
224 | {
225 | String shutDownShell = "echo " + SysConstants.ELEGANT_STOP_CONTENT + " > " + getShutDownFilePath();
226 | FileWriter fileWriter = null;
227 | try {
228 | File shellFile = new File(SysConstants.ELEGANT_SHUTDOWN_SHELL_SCRIPT + port + ".sh");
229 | if( ! shellFile.exists())
230 | {
231 | shellFile.createNewFile();
232 | }
233 | fileWriter = new FileWriter(shellFile);
234 | fileWriter.write(shutDownShell);
235 | fileWriter.flush();
236 | } catch (IOException e) {
237 | logger.error("", e);
238 | } finally {
239 | if(fileWriter != null)
240 | {
241 | try {
242 | fileWriter.close();
243 | } catch (IOException e) {
244 | logger.error(" ", e);
245 | }
246 | }
247 | }
248 | }
249 | }
250 |
251 | /**
252 | *
253 | * @Description: 启动检查是否关闭storage的线程
254 | */
255 | public void startCheckShutdownThread()
256 | {
257 | new Thread(
258 | new Runnable() {
259 | @Override
260 | public void run() {
261 |
262 | while(true)
263 | {
264 | if(isStopCurrentJavaInstance()) //是否需要停止
265 | {
266 | shutdown();
267 | //关闭该线程
268 | break;
269 | }
270 | try {
271 | Thread.sleep(SysConstants.CHECK_STOP_GAP_TIME);
272 | } catch (InterruptedException e) {
273 | logger.error("", e);
274 | }
275 | }
276 | }
277 | }
278 | ).start();
279 | }
280 |
281 | }
282 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/extend/action/MessageCounter.java:
--------------------------------------------------------------------------------
1 | package com.mogujie.ares.extend.action;
2 |
3 | import java.sql.SQLException;
4 | import java.util.Iterator;
5 | import java.util.List;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 |
9 | import com.mogujie.ares.data.Counter;
10 | import com.mogujie.ares.data.Group;
11 | import com.mogujie.ares.extend.BaseAction;
12 | import com.mogujie.ares.lib.logger.Logger;
13 | import com.mogujie.ares.lib.logger.LoggerFactory;
14 | import com.mogujie.ares.lib.net.DataBuffer;
15 | import com.mogujie.ares.model.CounterModel;
16 | import com.mogujie.ares.model.GroupModel;
17 | import com.mogujie.ares.model.MessageModel;
18 | import com.mogujie.ares.util.MoguUtil;
19 |
20 | public class MessageCounter extends BaseAction {
21 |
22 | private static final Logger logger = LoggerFactory
23 | .getLogger(MessageCounter.class);
24 |
25 | /*
26 | * 获得未读消息计数
27 | *
28 | * @param userId 请求的用户ID
29 | */
30 | public DataBuffer unread(int userId, DataBuffer attachment, int version) {
31 | logger.info("unread count: userId=" + userId);
32 | DataBuffer buffer = new DataBuffer();
33 | buffer.writeInt(userId);
34 |
35 | Counter userUnreadCount = CounterModel.getInstance().getUnreadMsgCount(
36 | userId);
37 | Map userUnreadInfo = userUnreadCount.getUnreadCount();
38 | String unreadCounterList = "";
39 | if (userUnreadInfo != null && userUnreadInfo.size() > 0) {
40 | buffer.writeInt(userUnreadInfo.size());
41 | Iterator> iter = userUnreadInfo
42 | .entrySet().iterator();
43 | int uid;
44 | int count;
45 | while (iter.hasNext()) {
46 | Map.Entry entry = iter.next();
47 | uid = Integer.valueOf(entry.getKey());
48 | count = Integer.valueOf(entry.getValue());
49 | buffer.writeInt(uid);
50 | buffer.writeInt(count);
51 | unreadCounterList += uid + "=" + count + ", ";
52 | }
53 | } else {
54 | buffer.writeInt(0);
55 | }
56 | logger.info("return unread count: userId = " + userId + " - "
57 | + unreadCounterList);
58 |
59 | return MoguUtil.writeAttachments(buffer, attachment);
60 | }
61 |
62 | /*
63 | * @auther ziye
64 | *
65 | * @Description: 清理消息计数,已读
66 | *
67 | * @param commandId
68 | *
69 | * @param requestId
70 | *
71 | * @param userId
72 | *
73 | * @param friendUserId
74 | *
75 | * @return
76 | */
77 | public DataBuffer clear(int commandId, int requestId, int userId,
78 | int friendUserId, int version) {
79 | logger.info("clear counter: requestId=" + requestId + ", userId="
80 | + userId + ", friendUserId=" + friendUserId);
81 |
82 | int result = 0;
83 | if (!CounterModel.getInstance().clearUserUnreadItemCount(userId,
84 | friendUserId)) {
85 | result = 1;
86 | }
87 | try {
88 | MessageModel.getInstance().deleteUserReadedDialogMessages(userId,
89 | friendUserId);
90 | } catch (SQLException e) {
91 | logger.error("", e);
92 | }
93 |
94 | DataBuffer responseBuffer = new DataBuffer();
95 | responseBuffer.writeInt(requestId);
96 | responseBuffer.writeInt(result);
97 | responseBuffer.writeChar((char) commandId);
98 | responseBuffer.writeInt(userId);
99 | responseBuffer.writeInt(friendUserId);
100 |
101 | return responseBuffer;
102 | }
103 |
104 | /*
105 | *
106 | * @Description: 获取群未读消息
107 | *
108 | * @param userId
109 | *
110 | * @param attachment
111 | *
112 | * @param version
113 | *
114 | * @return
115 | */
116 | public DataBuffer groupUnread(int userId, DataBuffer attachment, int version) {
117 | logger.info("group unread count: userId=" + userId);
118 | DataBuffer buffer = new DataBuffer();
119 | buffer.writeInt(userId);
120 |
121 | String values = "";
122 | try {
123 | List groups = GroupModel.getInstance().getGroupsByUserId(
124 | userId, false);
125 | if (groups != null && !groups.isEmpty()) {
126 | int groupCount = groups.size();
127 | int[] groupIds = new int[groupCount];
128 | Group groupInfo = null;
129 | for (int i = 0; i < groupCount; i++) {
130 | groupInfo = groups.get(i);
131 | if (groupInfo != null && groupInfo.getGroupId() > 0) {
132 | groupIds[i] = groupInfo.getGroupId();
133 | }
134 | }
135 | Map userUnreadCount = CounterModel
136 | .getInstance()
137 | .getUserGroupUnreadCount(userId, groupIds);
138 | if (userUnreadCount != null && !userUnreadCount.isEmpty()) {
139 | values = userUnreadCount.toString();
140 | DataBuffer tempDataBuffer = new DataBuffer();
141 | Iterator> it = userUnreadCount
142 | .entrySet().iterator();
143 | int groupId = 0;
144 | int count = 0;
145 | int num = 0;
146 | Entry entry = null;
147 | while (it.hasNext()) {
148 | entry = it.next();
149 | groupId = entry.getKey();
150 | count = entry.getValue();
151 | if (groupId > 0 && count > 0) {
152 | tempDataBuffer.writeInt(groupId);
153 | tempDataBuffer.writeInt(count);
154 | num++;
155 | }
156 | }
157 | buffer.writeInt(num);
158 | if (num > 0) {
159 | buffer.writeDataBuffer(tempDataBuffer);
160 | }
161 | } else {
162 | buffer.writeInt(0);
163 | }
164 | } else {
165 | buffer.writeInt(0);
166 | }
167 | } catch (SQLException e) {
168 | logger.error("group unread, userId" + userId, e);
169 | buffer.writeInt(0);
170 | }
171 |
172 | logger.info("return group unread count: userId = " + userId + " - "
173 | + values);
174 |
175 | return MoguUtil.writeAttachments(buffer, attachment);
176 | }
177 |
178 | /*
179 | * @auther ziye
180 | *
181 | * @Description: 清除用户在这个群的未读消息计数,全部置为已读
182 | *
183 | * @param commandId
184 | *
185 | * @param requestId
186 | *
187 | * @param userId
188 | *
189 | * @param friendUserId
190 | *
191 | * @return
192 | */
193 | public DataBuffer clearUserGroup(int commandId, int userId, int groupId,
194 | int version) {
195 | logger.info("clear counter: userId=" + userId + ", groupId=" + groupId);
196 |
197 | int result = 0;
198 | if (!CounterModel.getInstance().clearUserGroupCounter(userId, groupId)) {
199 | result = 1;
200 | }
201 |
202 | DataBuffer buffer = new DataBuffer();
203 | buffer.writeInt(0);
204 | buffer.writeInt(result);
205 | buffer.writeChar((char) commandId);
206 | buffer.writeInt(userId);
207 | buffer.writeInt(groupId);
208 |
209 | return buffer;
210 | }
211 |
212 | }
213 |
--------------------------------------------------------------------------------
/src/main/java/com/mogujie/ares/model/AudioModel.java:
--------------------------------------------------------------------------------
1 | /**
2 | * unread construct
3 | * userId => {
4 | * firendUserId => count
5 | * }
6 | * key construct : u + userId
7 | *
8 | * markread construct
9 | * userId => {
10 | * firendUserId => count
11 | * ...
12 | * }
13 | * key construct : m + userId
14 | *
15 | */
16 | package com.mogujie.ares.model;
17 |
18 | import java.sql.Connection;
19 | import java.sql.PreparedStatement;
20 | import java.sql.ResultSet;
21 | import java.sql.SQLException;
22 | import java.sql.Statement;
23 | import java.util.Arrays;
24 | import java.util.HashMap;
25 | import java.util.Iterator;
26 | import java.util.Map;
27 |
28 | import org.apache.commons.lang.StringUtils;
29 |
30 | import com.mogujie.ares.util.MoguArrayUtil;
31 | import com.mogujie.ares.util.MoguByteUtil;
32 | import com.mogujie.ares.data.Audio;
33 | import com.mogujie.ares.lib.logger.Logger;
34 | import com.mogujie.ares.lib.logger.LoggerFactory;
35 | import com.mogujie.ares.manager.DBManager;
36 | import com.mogujie.ares.manager.DBManager.DBPoolName;
37 | import com.mogujie.ares.manager.FileManager;
38 |
39 | /**
40 | *
41 | * @Description: 计数器相关操作
42 | * @author shitou - shitou[at]mogujie.com
43 | * @date 2013-7-22 下午2:19:39
44 | *
45 | */
46 | public class AudioModel
47 | {
48 |
49 | private static AudioModel instance;
50 | private static final Logger logger = LoggerFactory.getLogger(AudioModel.class);
51 |
52 |
53 | public static AudioModel getInstance()
54 | {
55 | if(instance == null)
56 | {
57 | instance = new AudioModel();
58 | }
59 | return instance;
60 | }
61 |
62 | private AudioModel(){}
63 |
64 | /**
65 | *
66 | * @Description: 保持一个语音文件并返回语音路径
67 | * @param bytes
68 | * @param userId
69 | * @param toUserId
70 | * @return 保持的audioid
71 | */
72 | public Audio saveAudio(int userId, int toUserId, Audio audio, int created) {
73 |
74 | if(audio == null || audio.getData() == null
75 | || audio.getData().length <= 0 || audio.getCostTime() <= 0) {
76 | return null;
77 | }
78 |
79 | byte[] data = audio.getData();
80 | String path = FileManager.getInstance().saveAudioBinary(data);
81 | if(StringUtils.isEmpty(path)) {
82 | return null;
83 | }
84 | audio.setCreated(created);
85 | audio.setFileSize(data.length);
86 | audio.setPath(path);
87 | audio.setUserId(userId);
88 | audio.setToUserId(toUserId);
89 | int id = saveAudioInfo(audio);
90 | audio.setId(id);
91 | return audio;
92 | }
93 |
94 | public Audio parseAudio(byte[] bytes) {
95 | if(bytes.length <= 4) { return null; }
96 | Audio audio = null;
97 | try {
98 | byte[] costByte = Arrays.copyOfRange(bytes, 0, 4);
99 | byte[] data = Arrays.copyOfRange(bytes, 4, bytes.length);
100 | int cost = MoguByteUtil.getInstance().convert2Int(costByte);
101 | if(cost <= 0) {
102 | return null;
103 | }
104 | audio = new Audio();
105 | audio.setCostTime(cost);
106 | audio.setData(data);
107 | } catch(Exception e) {
108 | return null;
109 | }
110 | return audio;
111 | }
112 |
113 | /**
114 | *
115 | * @Description: 数据库记录语音文件的路径
116 | * @param userId
117 | * @param toUserId
118 | * @param path
119 | * @param size
120 | * @param created
121 | * @return
122 | */
123 | private int saveAudioInfo(Audio audio) {
124 | DBManager dbManager = DBManager.getInstance();
125 | Connection conn = dbManager.getConnection(DBPoolName.macim_master);
126 | PreparedStatement statement = null;
127 | ResultSet rs = null;
128 | int succCount = 0;
129 | int id = 0;
130 | try {
131 | String sql = "insert into IMAudio(`userId`, `toUserId`, `path`, `fileSize`, `costTime`, `created`) " +
132 | "values(?, ?, ?, ?, ?, ?)";
133 | statement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
134 | int index = 1;
135 | statement.setInt(index++, audio.getUserId());
136 | statement.setInt(index++, audio.getToUserId());
137 | statement.setString(index++, audio.getPath());
138 | statement.setInt(index++, audio.getFileSize());
139 | statement.setInt(index++, audio.getCostTime());
140 | statement.setInt(index++, audio.getCreated());
141 | succCount = statement.executeUpdate();
142 | if(succCount > 0) {
143 | rs = statement.getGeneratedKeys();
144 | if(rs.next()) {
145 | id = rs.getInt(1);
146 | }
147 | }
148 | } catch (SQLException e) {
149 | logger.error(audio.getUserId() + " - " + audio.getToUserId() + " - " + audio.getPath(), e);
150 | } finally {
151 | dbManager.release(DBPoolName.macim_master, conn, statement, null);
152 | }
153 |
154 | return id;
155 | }
156 |
157 | /**
158 | *
159 | * @Description: 读取语音额外信息,除语音内容外
160 | * @param audioIds
161 | * @return
162 | */
163 | public Map getAudiosInfo(int[] audioIds) {
164 | Map audioMap = new HashMap();
165 | if(audioIds == null || audioIds.length == 0) {
166 | return audioMap;
167 | }
168 | DBManager dbManager = DBManager.getInstance();
169 | Connection conn = dbManager.getConnection(DBPoolName.macim_slave);
170 | PreparedStatement statement = null;
171 | ResultSet rs = null;
172 | try {
173 | StringBuilder sb = new StringBuilder("select * from IMAudio where id in (");
174 | int length = audioIds.length;
175 | int cnt = 0;
176 | for(int i = 0; i < length; i++) {
177 | if(audioIds[i] <= 0) {continue;}
178 | sb.append("?,");
179 | cnt++;
180 | }
181 | if(cnt <= 0) {
182 | return audioMap;
183 | }
184 | String sql = sb.subSequence(0, sb.length() - 1) + ")";
185 | statement = conn.prepareStatement(sql);
186 | int index = 1;
187 | for(int i = 0; i < length; i++) {
188 | if(audioIds[i] <= 0) {continue;}
189 | statement.setInt(index++, audioIds[i]);
190 | }
191 | rs = statement.executeQuery();
192 |
193 | Audio audio;
194 | while(rs.next()) {
195 | audio = new Audio();
196 | audio.setId(rs.getInt("id"));
197 | audio.setUserId(rs.getInt("userId"));
198 | audio.setToUserId(rs.getInt("toUserId"));
199 | audio.setPath(rs.getString("path"));
200 | audio.setFileSize(rs.getInt("fileSize"));
201 | audio.setCostTime(rs.getInt("costTime"));
202 | audio.setCreated(rs.getInt("created"));
203 | audioMap.put(audio.getId(), audio);
204 | }
205 | } catch (SQLException e) {
206 | logger.error(Arrays.toString(audioIds), e);
207 | } finally {
208 | dbManager.release(DBPoolName.macim_slave, conn, statement, rs);
209 | }
210 |
211 | return audioMap;
212 | }
213 |
214 | /**
215 | *
216 | * @Description: 读取一段语音的详细信息,包括语音文件内容
217 | * @param audioIds
218 | * @return
219 | */
220 | public Map readAudios(int[] audioIds) {
221 | Map audioMap = new HashMap();
222 | if(audioIds == null || audioIds.length == 0) {
223 | return audioMap;
224 | }
225 | audioIds = MoguArrayUtil.getInstance().arrayUnique(audioIds);
226 | audioMap = getAudiosInfo(audioIds);
227 | logger.info("read audios:" + Arrays.toString(audioIds));
228 | if(!audioMap.isEmpty()) {
229 | Iterator