在上一章分析的createClientListeners函数中构造了3个filter,ExecutorFilter、ProtocolCodecFilter和StalledSessionsFilter并将它们添加到mina框架中。这里首先贴出部分相关的源码,为了方便分析,首先贴出createClientListeners的部分源码,
ExecutorFilter executorFilter = new ExecutorFilter(getCorePoolSize(maxPoolSize), maxPoolSize, 60, TimeUnit.SECONDS);
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor)executorFilter.getExecutor();
ThreadFactory threadFactory = eventExecutor.getThreadFactory();
threadFactory = new DelegatingThreadFactory("C2S-Thread-", threadFactory);
eventExecutor.setThreadFactory(threadFactory);
socketAcceptor.getFilterChain().addFirst(EXECUTOR_FILTER_NAME, executorFilter);
socketAcceptor.getFilterChain().addAfter(EXECUTOR_FILTER_NAME, XMPP_CODEC_FILTER_NAME, new ProtocolCodecFilter(new XMPPCodecFactory()));
socketAcceptor.getFilterChain().addAfter(XMPP_CODEC_FILTER_NAME, CAPACITY_FILTER_NAME, new StalledSessionsFilter());
下面是startClientListeners的部分源码,
socketAcceptor.setHandler(new ClientConnectionHandler(serverName));
socketAcceptor.bind(new InetSocketAddress(bindInterface, port));
其中,ExecutorFilter和ProtocolCodecFilter是mina框架提供的两个过滤器,StalledSessionsFilter则是openfire自定义的一个过滤器,和接收消息没有太大关系,该过滤器用来处理一些关闭的Session。首先,ExecutorFilter和ProtocolCodecFilter的源码在《mina源码分析—4》中分析过了,可以知道,其中ProtocolCodecFilter最后会通过XMPPCodecFactory获得解码器,对消息进行解码。
XMPPCodecFactory的构造函数如下,
public XMPPCodecFactory() {
encoder = new XMPPEncoder();
decoder = new XMPPDecoder();
}
再来看XMPPDecoder的decode函数,
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
XMLLightweightParser parser = (XMLLightweightParser) session.getAttribute(ConnectionHandler.XML_PARSER);
parser.read(in);
if (parser.areThereMsgs()) {
for (String stanza : parser.getMsgs()) {
out.write(stanza);
}
}
return !in.hasRemaining();
}
这里Session中的XMLLightweightParser在何时设置的呢?在新添加某个Session时,handleNewSessions会调用addNow函数处理该session,在该函数中会调用IoServiceListenerSupport的fireSessionCreated函数,从而调用所有IoHandler的sessionOpened函数。在上面startClientListeners源码中设置了ClientConnectionHandler,其sessionOpened函数如下
public void sessionOpened(IoSession session) throws Exception {
final XMLLightweightParser parser = new XMLLightweightParser(CHARSET);
session.setAttribute(XML_PARSER, parser);
final NIOConnection connection = createNIOConnection(session);
session.setAttribute(CONNECTION, connection);
session.setAttribute(HANDLER, createStanzaHandler(connection));
final int idleTime = getMaxIdleTime() / 2;
if (idleTime > 0) {
session.getConfig().setIdleTime(IdleStatus.READER_IDLE, idleTime);
}
}
因此,doDecode函数中取出的parser就是XMLLightweightParser。接下来调用其read函数,读取IoBuffer中的数据,read的代码很长很长,这里就不往下做分析了,简单来说XMLLightweightParser就是一个openfire自己实现的XML解析器,输入是IoBuffer中的数据,其输出保存在成员变量msgs中,msgs的类型是字符串List。
回到doDecode函数中,接下来通过areThereMsgs判断是否有XML数据,如果有就调用ProtocolDecoderOutput(ProtocolDecoderOutputImpl)的write函数写入数据,write在其父类AbstractProtocolDecoderOutput中实现,如下
public void write(Object message) {
messageQueue.add(message);
}
这里就是简单的添加到消息队列中,然后如上一章最后所说,接下来从该消息队列中一个个取出消息,然后调用下一个过滤器进行处理。
根据前面几章的分析可知,通过了所有过滤器之后消息会到达最后一个过滤器TailFilter,该过滤器会开始调用注册的Handler处理消息,Handler包含了应用逻辑。openfire只注册了ClientConnectionHandler,下面来看,
public void messageReceived(IoSession session, Object message) throws Exception {
StanzaHandler handler = (StanzaHandler) session.getAttribute(HANDLER);
final XMPPPacketReader parser = PARSER_CACHE.get();
updateReadBytesCounter(session);
try {
handler.process((String) message, parser);
} catch (Exception e) {
}
}
从前面sessionOpened的函数中可知,这里的handler是通过createStanzaHandler函数构造的ClientStanzaHandler,
StanzaHandler createStanzaHandler(NIOConnection connection) {
return new ClientStanzaHandler(XMPPServer.getInstance().getPacketRouter(), serverName, connection);
}
其中connection是一个NIOConnection。
下面直接看ClientStanzaHandler的process函数,该函数较长,这里省略了大部分和flash、sasl、压缩、ack等等的相关代码,只留下2行最主要的语句,如下
public void process(String stanza, XMPPPacketReader reader) throws Exception {
...
Element doc = reader.read(new StringReader(stanza)).getRootElement();
...
process(doc);
}
这里的方法也不深究了,简而言之,就是根据输入的Xml字符串构造一个Document对象。重点看这里的process,
private void process(Element doc) throws UnauthorizedException {
String tag = doc.getName();
if ("message".equals(tag)) {
Message packet;
try {
packet = new Message(doc, !validateJIDs());
}
catch (IllegalArgumentException e) {
}
processMessage(packet);
}
else if ("presence".equals(tag)) {
Presence packet;
try {
packet = new Presence(doc, !validateJIDs());
}
catch (IllegalArgumentException e) {
}
try {
packet.getType();
}
catch (IllegalArgumentException e) {
}
try {
packet.getShow();
}
catch (IllegalArgumentException e) {
}
processPresence(packet);
}
else if ("iq".equals(tag)) {
IQ packet;
try {
packet = getIQ(doc);
}
catch (IllegalArgumentException e) {
}
processIQ(packet);
}
else {
}
}
这里根据每个消息的标签决定使用哪个函数进行处理,如果是一个message消息,则调用processMessage处理;如果是一个presence,则调用processPresence处理;如果是一个iq消息,则调用processIQ处理。