最近作了一些xmpp方面的東西,把它寫下來。html
最近選用了openfire作xmpp server。因此下面的內容都是基於openfire來作的。java
如今openfire最新的版本是3.7.1,下載下來,用eclipse來編譯運行。或者下載安裝包,能夠安裝在windows,Solaris上。windows
這兩種方式我都用了一下。oracle
關於在eclipse怎麼編譯運行openfire,能夠參考下面這個連接,寫的很全。照着作就能夠了。app
http://wenku.baidu.com/view/8acbbb305a8102d276a22fb5.html負載均衡
個人計劃是搭建一個HA的openfire系統。具體的講就是部署兩個openfire server,這兩個server的domain都配置成一個(這裏我用的是xmppservice)。而後在部署一個F5來作負載均衡和failover。這裏我沒有選擇openfire的cluster plugin。由於咱們是商業用途,cluster plugin須要買oracle coherence license。dom
個人邏輯業務用external component的方式來實現,並鏈接到openfire server上。 eclipse
在寫一個客戶端來測試整個系統。ide
下面主要把external component的實現和客戶端的實現記錄下來。測試
客戶端的實現很簡單,主要用到的包是smack。
import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.Message; public class XmppClient { public static void main(String args[]) throws XMPPException{ ConnectionConfiguration config = new ConnectionConfiguration(F5_ip_address,F5_port); XMPPConnection.DEBUG_ENABLED = true; XMPPConnection conn2 = new XMPPConnection(config); boolean auth = conn2.getSASLAuthentication().isAuthenticated(); try { conn2.connect(); } catch (XMPPException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(auth); try { conn2.login("client_name", "client_password"); } catch (XMPPException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } String service = conn2.getServiceName(); System.out.println(service); PacketListener listener = new SimplePacketListener(); PacketFilter filter = new SimplePacketFilter(); conn2.addPacketListener(listener, filter); buildMsg(conn2,i); System.out.println("finished"); } static void buildMsg(XMPPConnection conn2){ Message req = new Message(); req.setFrom("ilxlf@mymachine_name"); req.setTo("service1@service1.xmppservice"); req.setBody("This is client message"); System.out.println("message sent: "+req.toXML()); conn2.sendPacket(req); System.out.println("finished"); } }
這裏用到了兩個listener,如今沒有什麼太大的用途,主要用來顯示server的response。
import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.packet.Packet; public class SimplePacketFilter implements PacketFilter { @Override public boolean accept(Packet arg0) { // TODO Auto-generated method stub return true; } }
import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.packet.Packet; public class SimplePacketListener implements PacketListener { @Override public void processPacket(Packet arg0) { // TODO Auto-generated method stub System.out.println("received: "+arg0.toXML()); } }
下面來講說external component的實現。
先說說配置部署。當咱們寫了一個external component,咱們就須要把他鏈接到openfire server上。打開openfire的管理頁面,默認是在9090端口上。進入以後在server-->server setting -->external components裏面配置,默認鏈接到openfire的5275端口。而後咱們須要配置sub-domain和一些訪問控制策略。簡單點,把sub-domain配好就能夠用了(這裏我用service1表示sub-domain)。
爲了讓咱們的測試客戶端能鏈接上了,咱們在openfire上建立一個測試用戶。在Users/Groups-->create new user裏面。主要是用戶名和密碼。對應到上面的代碼就是"client_name","client_password"裏面的值。
下面就是具體的實現代碼。本質上external component能夠作成一個war應用,我這裏爲了方案的驗證就作一個普通的java應用。
這裏用到的包主要是tinder和whack。 一共三個包。
com.ilxlf.component
org.dom4j.io
org.xmpp.component
後兩個包看上去很奇怪,原本是不該該有的。可是在寫代碼的過程當中發現一些問題只能先這麼解決一下。
package com.ilxlf.component; import org.jivesoftware.whack.ExternalComponentManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.component.AbstractComponent; import org.xmpp.component.ComponentException; import org.xmpp.packet.JID; import org.xmpp.packet.Message; public class ExampleComponent extends AbstractComponent { private static final JID serviceAddress = new JID("service1@service1.xmppservice"); Logger log = LoggerFactory.getLogger(getClass()); public ExampleComponent(){ super(2,1000,true); } @Override public String getDomain() { // TODO Auto-generated method stub return "service1"; } @Override protected void handleMessage(Message message) { // TODO Auto-generated method stub //super.handleMessage(message); System.out.println(message.toString()); Message response = new Message(); response.setFrom(serviceAddress); response.setTo(message.getFrom()); response.setBody("give client a response....."); send(response); } @Override public String getDescription() { // TODO Auto-generated method stub return "This is pengfu first xmpp component."; } @Override public String getName() { // TODO Auto-generated method stub return "service1.xmppservice"; } public static void main(String args[]) throws ComponentException{ ExampleComponent comp = new ExampleComponent(); final ExternalComponentManager mgr = new ExternalComponentManager("xmppservice", 5275); mgr.setSecretKey("service1", "123456"); mgr.addComponent("service1", comp); while(true){ try { Thread.sleep(100000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
package org.dom4j.io; import org.dom4j.*; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.*; import java.net.URL; public class XPPPacketReader { /** * <code>DocumentFactory</code> used to create new document objects */ private DocumentFactory factory; /** * <code>XmlPullParser</code> used to parse XML */ private XmlPullParser xppParser; /** * <code>XmlPullParser</code> used to parse XML */ private XmlPullParserFactory xppFactory; /** * DispatchHandler to call when each <code>Element</code> is encountered */ private DispatchHandler dispatchHandler; public XPPPacketReader() { } public XPPPacketReader(DocumentFactory factory) { this.factory = factory; } /** * <p>Reads a Document from the given <code>File</code></p> * * @param file is the <code>File</code> to read from. * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. * @throws java.net.MalformedURLException if a URL could not be made for the given File */ public Document read(File file) throws DocumentException, IOException, XmlPullParserException { String systemID = file.getAbsolutePath(); return read(new BufferedReader(new FileReader(file)), systemID); } /** * <p>Reads a Document from the given <code>URL</code></p> * * @param url <code>URL</code> to read from. * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(URL url) throws DocumentException, IOException, XmlPullParserException { String systemID = url.toExternalForm(); return read(createReader(url.openStream()), systemID); } /** * <p>Reads a Document from the given URL or filename.</p> * <p/> * <p/> * If the systemID contains a <code>':'</code> character then it is * assumed to be a URL otherwise its assumed to be a file name. * If you want finer grained control over this mechansim then please * explicitly pass in either a {@link URL} or a {@link File} instance * instead of a {@link String} to denote the source of the document. * </p> * * @param systemID is a URL for a document or a file name. * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. * @throws java.net.MalformedURLException if a URL could not be made for the given File */ public Document read(String systemID) throws DocumentException, IOException, XmlPullParserException { if (systemID.indexOf(':') >= 0) { // lets assume its a URL return read(new URL(systemID)); } else { // lets assume that we are given a file name return read(new File(systemID)); } } /** * <p>Reads a Document from the given stream</p> * * @param in <code>InputStream</code> to read from. * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(InputStream in) throws DocumentException, IOException, XmlPullParserException { return read(createReader(in)); } /** * <p>Reads a Document from the given <code>Reader</code></p> * * @param reader is the reader for the input * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(Reader reader) throws DocumentException, IOException, XmlPullParserException { getXPPParser().setInput(reader); return parseDocument(); } /** * <p>Reads a Document from the given array of characters</p> * * @param text is the text to parse * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(char[] text) throws DocumentException, IOException, XmlPullParserException { getXPPParser().setInput(new CharArrayReader(text)); return parseDocument(); } /** * <p>Reads a Document from the given stream</p> * * @param in <code>InputStream</code> to read from. * @param systemID is the URI for the input * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(InputStream in, String systemID) throws DocumentException, IOException, XmlPullParserException { return read(createReader(in), systemID); } /** * <p>Reads a Document from the given <code>Reader</code></p> * * @param reader is the reader for the input * @param systemID is the URI for the input * @return the newly created Document instance * @throws DocumentException if an error occurs during parsing. */ public Document read(Reader reader, String systemID) throws DocumentException, IOException, XmlPullParserException { Document document = read(reader); document.setName(systemID); return document; } // Properties //------------------------------------------------------------------------- public XmlPullParser getXPPParser() throws XmlPullParserException { if (xppParser == null) { xppParser = getXPPFactory().newPullParser(); } return xppParser; } public XmlPullParserFactory getXPPFactory() throws XmlPullParserException { if (xppFactory == null) { xppFactory = XmlPullParserFactory.newInstance(); } xppFactory.setNamespaceAware(true); return xppFactory; } public void setXPPFactory(XmlPullParserFactory xppFactory) { this.xppFactory = xppFactory; } /** * @return the <code>DocumentFactory</code> used to create document objects */ public DocumentFactory getDocumentFactory() { if (factory == null) { factory = DocumentFactory.getInstance(); } return factory; } /** * <p>This sets the <code>DocumentFactory</code> used to create new documents. * This method allows the building of custom DOM4J tree objects to be implemented * easily using a custom derivation of {@link DocumentFactory}</p> * * @param factory <code>DocumentFactory</code> used to create DOM4J objects */ public void setDocumentFactory(DocumentFactory factory) { this.factory = factory; } /** * Adds the <code>ElementHandler</code> to be called when the * specified path is encounted. * * @param path is the path to be handled * @param handler is the <code>ElementHandler</code> to be called * by the event based processor. */ public void addHandler(String path, ElementHandler handler) { getDispatchHandler().addHandler(path, handler); } /** * Removes the <code>ElementHandler</code> from the event based * processor, for the specified path. * * @param path is the path to remove the <code>ElementHandler</code> for. */ public void removeHandler(String path) { getDispatchHandler().removeHandler(path); } /** * When multiple <code>ElementHandler</code> instances have been * registered, this will set a default <code>ElementHandler</code> * to be called for any path which does <b>NOT</b> have a handler * registered. * * @param handler is the <code>ElementHandler</code> to be called * by the event based processor. */ public void setDefaultHandler(ElementHandler handler) { getDispatchHandler().setDefaultHandler(handler); } // Implementation methods //------------------------------------------------------------------------- public Document parseDocument() throws DocumentException, IOException, XmlPullParserException { DocumentFactory df = getDocumentFactory(); Document document = df.createDocument(); Element parent = null; XmlPullParser pp = getXPPParser(); int count = 0; while (true) { int type = -1; type = pp.nextToken(); switch (type) { case XmlPullParser.PROCESSING_INSTRUCTION: { String text = pp.getText(); int loc = text.indexOf(" "); if (loc >= 0) { document.addProcessingInstruction(text.substring(0, loc), text.substring(loc + 1)); } else document.addProcessingInstruction(text, ""); break; } case XmlPullParser.COMMENT: { if (parent != null) parent.addComment(pp.getText()); else document.addComment(pp.getText()); break; } case XmlPullParser.CDSECT: { String text = pp.getText(); if (parent != null) { parent.addCDATA(text); } else { if (text.trim().length() > 0) { throw new DocumentException("Cannot have text content outside of the root document"); } } break; } case XmlPullParser.ENTITY_REF: { String text = pp.getText(); if (parent != null) { parent.addText(text); } else { if (text.trim().length() > 0) { throw new DocumentException("Cannot have an entityref outside of the root document"); } } break; } case XmlPullParser.END_DOCUMENT: { return document; } case XmlPullParser.START_TAG: { QName qname = (pp.getPrefix() == null) ? df.createQName(pp.getName(), pp.getNamespace()) : df.createQName(pp.getName(), pp.getPrefix(), pp.getNamespace()); Element newElement = null; // Do not include the namespace if this is the start tag of a new packet // This avoids including "jabber:client", "jabber:server" or // "jabber:component:accept" if ("jabber:client".equals(qname.getNamespaceURI()) || "jabber:server".equals(qname.getNamespaceURI()) || "jabber:component:accept".equals(qname.getNamespaceURI()) || "http://jabber.org/protocol/httpbind".equals(qname.getNamespaceURI())) { newElement = df.createElement(pp.getName()); } else { newElement = df.createElement(qname); } int nsStart = pp.getNamespaceCount(pp.getDepth() - 1); int nsEnd = pp.getNamespaceCount(pp.getDepth()); for (int i = nsStart; i < nsEnd; i++) if (pp.getNamespacePrefix(i) != null) newElement.addNamespace(pp.getNamespacePrefix(i), pp.getNamespaceUri(i)); for (int i = 0; i < pp.getAttributeCount(); i++) { QName qa = (pp.getAttributePrefix(i) == null) ? df.createQName(pp.getAttributeName(i)) : df.createQName(pp.getAttributeName(i), pp.getAttributePrefix(i), pp.getAttributeNamespace(i)); newElement.addAttribute(qa, pp.getAttributeValue(i)); } if (parent != null) { parent.add(newElement); } else { document.add(newElement); } parent = newElement; count++; break; } case XmlPullParser.END_TAG: { if (parent != null) { parent = parent.getParent(); } count--; if (count < 1) { return document; } break; } case XmlPullParser.TEXT: { String text = pp.getText(); if (parent != null) { parent.addText(text); } else { if (text.trim().length() > 0) { throw new DocumentException("Cannot have text content outside of the root document"); } } break; } default: { ; } } } } protected DispatchHandler getDispatchHandler() { if (dispatchHandler == null) { dispatchHandler = new DispatchHandler(); } return dispatchHandler; } protected void setDispatchHandler(DispatchHandler dispatchHandler) { this.dispatchHandler = dispatchHandler; } /** * Factory method to create a Reader from the given InputStream. */ protected Reader createReader(InputStream in) throws IOException { return new BufferedReader(new InputStreamReader(in)); } } /* * Redistribution and use of this software and associated documentation * ("Software"), with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain copyright * statements and notices. Redistributions must also contain a * copy of this document. * * 2. Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. The name "DOM4J" must not be used to endorse or promote * products derived from this Software without prior written * permission of MetaStuff, Ltd. For written permission, * please contact dom4j-info@metastuff.com. * * 4. Products derived from this Software may not be called "DOM4J" * nor may "DOM4J" appear in their names without prior written * permission of MetaStuff, Ltd. DOM4J is a registered * trademark of MetaStuff, Ltd. * * 5. Due credit should be given to the DOM4J Project - * http://www.dom4j.org * * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Copyright 2001-2004 (C) MetaStuff, Ltd. All Rights Reserved. * * $Id: XPPPacketReader.java 6408 2006-12-15 23:35:30Z gato $ */
package org.xmpp.component; public interface Log { }
用到的jar包是:
下一步的工做:
若是一個server鏈接多個相同業務的external component,是否能夠在這些component之間作負載均衡和failover。
openfire的pub-sub的使用。
dd