openfire源码分析---5

filter源码分析

在上一章分析的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

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);
    }

这里就是简单的添加到消息队列中,然后如上一章最后所说,接下来从该消息队列中一个个取出消息,然后调用下一个过滤器进行处理。

ClientConnectionHandler

根据前面几章的分析可知,通过了所有过滤器之后消息会到达最后一个过滤器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处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值