openfire插件–外部組件開發
與外部交互
外部組件
openfire外部組件,就是與openfire所在的進程沒有關係,甚至可以不與openfire在同一臺機器上運行的組件,一般是一個可運行的jar包,我們叫做外部組件,使用tcp連接(類似客戶端Socket的監聽端口)與openfire之間進行通信,處理一些服務器需要處理的邏輯。目前可以使用Whack
和Tinder
連接操作。
Openfire的外部組件使用tcp連接與openfire之間進行通信。
- 發送消息到openfire,格式形如
<message to="x.myopenfire.com">內容</message>
。 - Openfire收到這條消息,發現了有一個二級域名
x.myopenfire.com
,它會在外部組件中尋找誰會處理這個域名的消息,發現了該外部組件並會轉發給外部組件。 - 外部組件接受到這條消息,處理後再發回openfire,格式形如
<message to="[email protected]" from="x.myopenfire.com">內容</message>
。 - openfire發到用戶上。
配置
先配置子域名和密碼,如下服務器端口默認爲5275,子域名爲chat,密碼爲123456。這裏操作的是ofExtComponentConf
數據庫表。
Java
外部組件使用Whack
(需自行編譯jar包,ver2.0.1)連接openfire,類似Socket監聽服務器端口。注意必須在openfire啓動後在啓動外部組件,不然會報錯。這種方式相當於轉發。
org.xmpp.component.ComponentException: internal-server-error
import org.jivesoftware.whack.ExternalComponentManager;
import org.xmpp.component.ComponentException;
public class Main {
public static void main(String[] args) {
ExternalComponentManager mgr = new ExternalComponentManager(
"192.168.43.42", 5275); //openfire的IP和相應的端口
mgr.setSecretKey("chat", "123456"); //設置子域和口令(密碼)
try {
mgr.addComponent("chat", new ChatComponent());
} catch (ComponentException e) {
e.printStackTrace();
System.exit(-1);
}
// daemon
while (true) {
try {
Thread.sleep(10000);
} catch (Exception e) {
}
}
}
}
業務邏輯實現Component,在public void processPacket(Packet packet)
處理具體數據。
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
public class ChatComponent implements Component{
private static final String name="chat";
private static final String description="實現多人通過組件在區域內聊天";
private JID jid;
private ComponentManager comMgr;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public void processPacket(Packet packet) {
System.out.println(packet.toXML());
}
public void initialize(JID jid, ComponentManager componentManager)
throws ComponentException {
this.jid=jid;
this.comMgr=componentManager;
}
public void start() {
System.out.println("component start");
}
public void shutdown() {
System.out.println("component shutdown");
}
}
通過客戶端Spark登錄發送hello,會接收到如下數據(前者爲心跳,後者爲實際文本數據)
K3ekQ1
helloK3ekQ1
主動發送消息 //TODO接收組件發送的消息失敗
package xmpp;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
import org.jivesoftware.smack.chat2.OutgoingChatMessageListener;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import java.io.IOException;
public class Test {
public static void main(String args[]) throws XMPPException {
try{
//smack ver4.2.3
//實質上時兩個用戶間的通信,其中一方爲虛擬用戶,代表的時外部組件
XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
.setUsernameAndPassword("a", "a")
.setXmppDomain("wang")
.setHost("wang")
.setPort(5222)
//非安全模式下,不校驗證書(todo:安全模式下的)
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setDebuggerEnabled(true)
.build();
AbstractXMPPConnection connection = new XMPPTCPConnection(config);
connection.connect().login();
ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addIncomingListener(new IncomingChatMessageListener() {
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
System.out.println("New message from " + from + ": " + message.getBody());
}
});
chatManager.addOutgoingListener(new OutgoingChatMessageListener() {
public void newOutgoingMessage(EntityBareJid to, Message message, Chat chat) {
System.out.println("New message to " + to + ": " + message.getBody());
}
});
EntityBareJid jid = JidCreate.entityBareFrom("[email protected]");
Chat chat = chatManager.chatWith(jid);
chat.send("Howdy!");
while(true){
Thread.sleep(1000);
}
} catch (InterruptedException e1) {
e1.printStackTrace();
} catch (XmppStringprepException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (SmackException e1) {
e1.printStackTrace();
}
}
}
//TODO 安全模式下發送消息
https://github.com/escline/InstallCert
在%JRE_HOME%/bin 執行如下命令,添加證書,密鑰庫口令爲changeit
keytool -keystore ..\lib\security\cacerts -import -alias myKey -file C:\env\tomcat\wpgl\usr\myKey.crt
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
NodeJS
openfire裏面有插件,但只是負責啓動NodeJS的應用,不負責數據交互。
和外部組件通信,也是基於用戶間的交流
<!DOCTYPE html>
<html>
<head>
<title>Latest content</title>
<script src='http://cdn.bootcss.com/jquery/1.9.1/jquery.min.js'></script>
<script src='http://cdn.bootcss.com/strophe.js/1.1.3/strophe.min.js'></script>
<script type="text/javascript">
var BOSH_SERVICE = 'http://wang:7070/http-bind/';
var connection = null;
function log(msg)
{
$('#log').append('<div></div>').append(document.createTextNode(msg));
}
/**
* 連接綁定方法
* @param status
*/
function onConnect(status)
{
if (status == Strophe.Status.CONNECTING) {
log('Strophe is connecting.');
} else if (status == Strophe.Status.CONNFAIL) {
log('Strophe failed to connect.');
$('#connect').get(0).value = 'connect';
} else if (status == Strophe.Status.DISCONNECTING) {
log('Strophe is disconnecting.');
} else if (status == Strophe.Status.DISCONNECTED) {
log('Strophe is disconnected.');
$('#connect').get(0).value = 'connect';
} else if (status == Strophe.Status.CONNECTED) {
log('Strophe is connected.');
log('ECHOBOT: Send a message to ' + connection.jid +
' to talk to me.');
connection.addHandler(onMessage, null, 'message', null, null, null);
connection.send($pres().tree());
}
}
/**
* 獲取消息時的方法
* @param msg
* @returns {Boolean}
*/
function onMessage(msg) {
var to = msg.getAttribute('to');
var from = msg.getAttribute('from');
var type = msg.getAttribute('type');
var elems = msg.getElementsByTagName('body');
if (type == "chat" && elems.length > 0) {
var body = elems[0];
log('ECHOBOT: I got a message from ' + from + ': ' +
Strophe.getText(body));
/* 關閉echo機器的自動回覆
var reply = $msg({to: from, from: to, type: 'chat'})
.cnode(Strophe.copyElement(body));
connection.send(reply.tree());
log('ECHOBOT: I sent ' + from + ': ' + Strophe.getText(body));*/
}
return true;
}
/**
* 發信息
* @param toId
* @param fromId
* @param msg
*/
function sendMsg(toId,fromId,msg) {
var reply = $msg({to: toId, from:fromId , type: 'chat'}).cnode(Strophe.xmlElement('body', '' ,msg));
connection.send(reply.tree());
log('ECHOBOT: I sent ' + toId + ': ' + msg);
}
/**
* 事件監聽
*/
$(document).ready(function () {
connection = new Strophe.Connection(BOSH_SERVICE);
// Uncomment the following lines to spy on the wire traffic.
connection.rawInput = function (data) { console.log('RECV: ' + data); };
connection.rawOutput = function (data) { console.log('SEND: ' + data); };
// Uncomment the following line to see all the debug output.
//Strophe.log = function (level, msg) { log('LOG: ' + msg); };
$('#connect').bind('click', function () {
var button = $('#connect').get(0);
if (button.value == 'connect') {
button.value = 'disconnect';
connection.connect($('#jid').get(0).value,
$('#pass').get(0).value,
onConnect);
} else {
button.value = 'connect';
connection.disconnect();
}
});
$('#replay').bind('click', function () {
toId = $('#tojid').val();
fromId = $('#jid').val();
msg=$('#msg').val();
sendMsg(toId,fromId,msg);
});
});
</script>
</head>
<body>
<div id='login' style='text-align: left'>
<form name='cred'>
<label for='jid'>JID:</label>
<input type='text' id='jid' value="a@wang" /><br/>
<label for='pass'>Password:</label>
<input type='password' id='pass' value="a" /><br/>
<input type='button' id='connect' value='connect' />
</form>
</div>
<hr />
<div style="text-align: left">
<label for='tojid'>tojid</label>
<input type='text' id='tojid' value="" /><br/>
<label for='msg'>msg:</label>
<textarea id="msg">Hello world!!!</textarea>
<br/>
<input type='button' id='replay' value='replay' />
</div>
<hr />
<div id='log'></div>
</body>
</html>
NodeJS和Java通過Thrift進行RPC通信.。
附相應例子:Thrift–實現NodeJS和Java間通信