曾经有一个同学,在网上问我,如果想androidpn添加额外的xmpp协议的方法在怎么加呢?我当时很迷惑,后来经过一翻仔细研究androidpn发现,其实每一种处理xmpp协议方法,必须有一个Handler实现。具体可以参考org.androidpn.server.xmpp.handler中。
针对每一个Handler对应的xml拥有不同的命名空间,每一个命名空间在xmpp中都有定义,因为传输的xml的格式是一定的。
例如:
IQAuthHandler:命名空间 String NAMESPACE = "jabber:iq:auth";
IQRegisterHandler:命名空间 String NAMESPACE = "jabber:iq:register";
IQRosterHandler:命名空间 String NAMESPACE = "jabber:iq:roster";
同时我们从Handler的实现可以看到每一个handler最好实现对应IQHandler类,但是为辅助类型的Handler那么可以不用,例如androidpn的状态更新处理器类PresenceUpdateHandler,不必要发送相关的消息到客户端。
所以如果要实现xmpp中如查看好友,用户分组等通信协议,那么你可能要实现相关的Handler并使用xmpp协议规定的相关的命名空间。
在androidpn中主要的业务处理类XmppIoHandler可以看出最终消息解析之后分发到IQRouter中。IQRouter用于处理消息的响应的消息。
IQRouter的源代码如下:
package org.androidpn.server.xmpp.router; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.androidpn.server.xmpp.handler.IQAuthHandler; import org.androidpn.server.xmpp.handler.IQHandler; import org.androidpn.server.xmpp.handler.IQRegisterHandler; import org.androidpn.server.xmpp.handler.IQRosterHandler; import org.androidpn.server.xmpp.session.ClientSession; import org.androidpn.server.xmpp.session.Session; import org.androidpn.server.xmpp.session.SessionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Element; import org.xmpp.packet.IQ; import org.xmpp.packet.JID; import org.xmpp.packet.PacketError; /** * This class is to route IQ packets to their corresponding handler. * * @author Sehwan Noh ([email protected]) */ public class IQRouter { private final Log log = LogFactory.getLog(getClass()); private SessionManager sessionManager; private List<IQHandler> iqHandlers = new ArrayList<IQHandler>(); private Map<String, IQHandler> namespace2Handlers = new ConcurrentHashMap<String, IQHandler>(); /** * Constucts a packet router registering new IQ handlers. */ public IQRouter() { sessionManager = SessionManager.getInstance(); iqHandlers.add(new IQAuthHandler()); iqHandlers.add(new IQRegisterHandler()); iqHandlers.add(new IQRosterHandler()); } /** * Routes the IQ packet based on its namespace. * * @param packet the packet to route */ public void route(IQ packet) { if (packet == null) { throw new NullPointerException(); } JID sender = packet.getFrom(); ClientSession session = sessionManager.getSession(sender); if (session == null || session.getStatus() == Session.STATUS_AUTHENTICATED || ("jabber:iq:auth".equals(packet.getChildElement() .getNamespaceURI()) || "jabber:iq:register".equals(packet.getChildElement() .getNamespaceURI()) || "urn:ietf:params:xml:ns:xmpp-bind" .equals(packet.getChildElement().getNamespaceURI()))) { handle(packet); } else { IQ reply = IQ.createResultIQ(packet); reply.setChildElement(packet.getChildElement().createCopy()); reply.setError(PacketError.Condition.not_authorized); session.process(reply); } } private void handle(IQ packet) { try { Element childElement = packet.getChildElement(); String namespace = null; if (childElement != null) { namespace = childElement.getNamespaceURI(); } if (namespace == null) { if (packet.getType() != IQ.Type.result && packet.getType() != IQ.Type.error) { log.warn("Unknown packet " + packet); } } else { IQHandler handler = getHandler(namespace); if (handler == null) { sendErrorPacket(packet, PacketError.Condition.service_unavailable); } else { handler.process(packet); } } } catch (Exception e) { log.error("Could not route packet", e); Session session = sessionManager.getSession(packet.getFrom()); if (session != null) { IQ reply = IQ.createResultIQ(packet); reply.setError(PacketError.Condition.internal_server_error); session.process(reply); } } } /** * Senda the error packet to the original sender */ private void sendErrorPacket(IQ originalPacket, PacketError.Condition condition) { if (IQ.Type.error == originalPacket.getType()) { log.error("Cannot reply an IQ error to another IQ error: " + originalPacket); return; } IQ reply = IQ.createResultIQ(originalPacket); reply.setChildElement(originalPacket.getChildElement().createCopy()); reply.setError(condition); try { PacketDeliverer.deliver(reply); } catch (Exception e) { // Ignore } } /** * Adds a new IQHandler to the list of registered handler. * * @param handler the IQHandler */ public void addHandler(IQHandler handler) { if (iqHandlers.contains(handler)) { throw new IllegalArgumentException( "IQHandler already provided by the server"); } namespace2Handlers.put(handler.getNamespace(), handler); } /** * Removes an IQHandler from the list of registered handler. * * @param handler the IQHandler */ public void removeHandler(IQHandler handler) { if (iqHandlers.contains(handler)) { throw new IllegalArgumentException( "Cannot remove an IQHandler provided by the server"); } namespace2Handlers.remove(handler.getNamespace()); } /** * Returns an IQHandler with the given namespace. */ private IQHandler getHandler(String namespace) { IQHandler handler = namespace2Handlers.get(namespace); if (handler == null) { for (IQHandler handlerCandidate : iqHandlers) { if (namespace.equalsIgnoreCase(handlerCandidate.getNamespace())) { handler = handlerCandidate; namespace2Handlers.put(namespace, handler); break; } } } return handler; } }
由以上的源代码可以看出,IQRouter在加载时候将各种处理器添加到回话管理器中,当消息分发到IQRouter中时候,根据命名空间的不同使用不同的处理处置即可。