tomee
我有一个需要在完成某些工作时通知用户的应用程序。 它使用JSF和Primefaces,因此可以使用大气 (也称为Push)来实现这种通知。
但是另一个有趣的方法是使用嵌入在Java Web应用程序中的XMPP服务器。 好的,好的,您不必嵌入它,您只需运行一个工业级XMPP服务器的实例,例如Openfire和Tigase即可 。 但是,嘿,我们只是玩了一点,所以我将向您展示如何使用Vysper进行操作, Vysper是使用Apache Mina开发的概念证明,只需几分钟即可轻松完成。
在展示如何做之前,很高兴记住线程和JEE应用程序通常不会混合使用 ,因此我们可以玩,但是我们必须知道我们在做什么。
首先,您将需要这些JAR,其中大多数来自Vysper。 只是几个?
- aopalliance-1.0.jar
- commons-codec-1.4.jar
- commons-collections-3.1.jar
- commons-io-1.4.jar
- commons-lang-2.5.jar
- commons-logging-1.1.jar
- 并发1.3.4.jar
- derby-10.2.1.6.jar
- dnsjava-2.0.8.jar
- ehcache-core-2.2.0.jar
- fontbox-0.1.0.jar
- jackrabbit-api-1.5.0.jar
- jackrabbit-core-1.5.3.jar
- jackrabbit-jcr-commons-1.5.3.jar
- jackrabbit-spi-1.5.0.jar
- jackrabbit-spi-commons-1.5.0.jar
- jackrabbit-text-extractors-1.5.0.jar
- jcl-over-slf4j-1.5.3.jar
- jcr-1.0.jar
- jempbox-0.2.0.jar
- jetty-continuation-7.2.1.v20101111.jar
- jetty-http-7.2.1.v20101111.jar
- jetty-io-7.2.1.v20101111.jar
- jetty-security-7.2.1.v20101111.jar
- jetty-server-7.2.1.v20101111.jar
- jetty-servlet-7.2.1.v20101111.jar
- jetty-util-7.2.1.v20101111.jar
- jetty-websocket-7.2.1.v20101111.jar
- log4j-1.2.14.jar
- lucene-core-2.3.2.jar
- mina-core-2.0.2.jar
- nbxml-0.7.jar
- nekohtml-1.9.7.jar
- pdfbox-0.7.3.jar
- poi-3.0.2-FINAL.jar
- poi-scratchpad-3.0.2-FINAL.jar
- primefaces-4.0.jar
- servlet-api-2.5.jar
- slf4j-api-1.5.3.jar
- slf4j-log4j12-1.5.3.jar
- smack-3.1.0.jar
- smackx-3.1.0.jar
- spec-compliance-0.7.jar
- 弹簧-aop-3.0.5.RELEASE.jar
- 弹簧-asm-3.0.5.RELEASE.jar
- Spring Bean3.0.5.RELEASE.jar
- 弹簧上下文-3.0.5.RELEASE.jar
- 弹簧核心-3.0.5.RELEASE.jar
- spring-expression-3.0.5.RELEASE.jar
- vysper-core-0.7.jar
- vysper-websockets-0.7.jar
- xep0045-muc-0.7.jar
- xep0060-pubsub-0.7.jar
- xep0124-xep0206-bosh-0.7.jar
- xercesImpl-2.8.1.jar
- xml-apis-1.3.03.jar
现在,从Vysper复制虚假证书,以便您的XMPP服务器可以在“安全”通道下“工作”。 它称为bogus_mina_tls.cert。
我的xhtml看起来像这样:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Messaging Prototype</title>
<link rel="icon" type="image/png" href="favicon.ico"></link>
</h:head>
<h:body>
<h:outputStylesheet library="css" name="style.css" />
<p:ajaxStatus style="width:16px;height:16px;" id="ajaxStatusPanel">
<f:facet name="start">
<h:graphicImage value="./ajaxloading.gif" />
</f:facet>
<f:facet name="complete">
<h:outputText value="" />
</f:facet>
</p:ajaxStatus>
<h:form>
<p:messages id="messages" showDetail="true" autoUpdate="true" closable="true" />
<p:spacer height="10" />
<p:panel>
<h:panelGrid columns="2">
<p:commandButton value="Enter" action="#{messagingMB.sendMessage}" />
</h:panelGrid>
</p:panel>
<p:spacer height="10" />
</h:form>
</h:body>
</html>
很简单吧? 托管Bean也很容易。
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import org.jivesoftware.smack.XMPPException;
@ManagedBean
@ViewScoped
public class MessagingMB implements Serializable {
private static final long serialVersionUID = -9092497421080796430L;
@EJB
private JSFUtilEJB jsfUtilEJB;
@PostConstruct
public void init() {
}
public void sendMessage() {
try {
new BasicClient().test();
} catch (XMPPException e) {
jsfUtilEJB.addErrorMessage(e,"Could not send");
}
}
}
当然,EJB:
import javax.ejb.Stateless;
import javax.faces.application.FacesMessage;
import javax.faces.application.FacesMessage.Severity;
import javax.faces.context.FacesContext;
@Stateless
public class JSFUtilEJB {
@SuppressWarnings("unchecked")
public <T> T findBean(String beanName) {
FacesContext context = FacesContext.getCurrentInstance();
return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class);
}
public long addErrorMessage(String msg) {
return addMessage(null,FacesMessage.SEVERITY_ERROR,msg);
}
public long addErrorMessage(Exception e,String summary){
return addMessage(e,FacesMessage.SEVERITY_ERROR,summary);
}
public long addFatalErrorMessage(Exception e,String summary){
return addMessage(e,FacesMessage.SEVERITY_FATAL,summary);
}
public long addInfoMessage(String summary){
return addMessage(null,FacesMessage.SEVERITY_INFO,summary);
}
public long addWarnMessage(Exception e,String summary){
return addMessage(e,FacesMessage.SEVERITY_WARN,summary);
}
public long addErrorMessage(Exception e) {
return addMessage(e,FacesMessage.SEVERITY_ERROR,e.getMessage(),e.getClass().getSimpleName());
}
private long addMessage(Exception e,Severity severity, String summary) {
FacesContext context = FacesContext.getCurrentInstance();
String clientId = null;
long id = -1;
if (e != null){
id = printStackTrace(e);
FacesMessage facesMessage = null;
if (e.getCause() instanceof org.apache.openjpa.persistence.EntityExistsException){
facesMessage = new FacesMessage(severity,"[Error: #"+id+"] "+summary,"You are trying are to add a new object that already exists or your're trying to violate a unique constraint)" );
}else{
facesMessage = new FacesMessage(severity,"[Error: #"+id+"] "+summary,e.getMessage() );
}
context.addMessage(clientId , facesMessage );
}else{
FacesMessage facesMessage = new FacesMessage(severity,summary," ");
context.addMessage(clientId , facesMessage );
}
return id;
}
private long addMessage(Exception e,Severity severity, String summary, String detail) {
FacesContext context = FacesContext.getCurrentInstance();
String clientId = null;
long id = -1;
if (e != null){
id = printStackTrace(e);
FacesMessage facesMessage = new FacesMessage(severity,"["+id+"] "+summary,detail );
context.addMessage(clientId , facesMessage );
}else{
FacesMessage facesMessage = new FacesMessage(severity,summary,detail );
context.addMessage(clientId , facesMessage );
}
return id;
}
public long printStackTrace(Exception e){
long uniqueId = System.currentTimeMillis();
return uniqueId;
}
}
和
import java.io.File;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.apache.vysper.mina.TCPEndpoint;
import org.apache.vysper.storage.StorageProviderRegistry;
import org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry;
import org.apache.vysper.xmpp.addressing.EntityImpl;
import org.apache.vysper.xmpp.authorization.AccountManagement;
import org.apache.vysper.xmpp.server.XMPPServer;
@Startup
@Singleton
public class XmppEJB implements Serializable {
/**
* <br>06/09/2014
*/
private static final long serialVersionUID = 1L;
private boolean started;
@PostConstruct
public void init() {
try {
// choose the storage you want to use
// StorageProviderRegistry providerRegistry = new JcrStorageProviderRegistry();
StorageProviderRegistry providerRegistry = new MemoryStorageProviderRegistry();
final AccountManagement accountManagement = (AccountManagement) providerRegistry.retrieve(AccountManagement.class);
if(!accountManagement.verifyAccountExists(EntityImpl.parse("user1@vysper.org"))) {
accountManagement.addUser(EntityImpl.parse("user1@vysper.org"), "password");
}
if(!accountManagement.verifyAccountExists(EntityImpl.parse("user2@vysper.org"))) {
accountManagement.addUser(EntityImpl.parse("user2@vysper.org"), "password");
}
XMPPServer server = new XMPPServer("vysper.org");
server.addEndpoint(new TCPEndpoint());
server.setStorageProviderRegistry(providerRegistry);
server.setTLSCertificateInfo(new File("/path/to/bogus_mina_tls.cert"), "boguspw");
server.start();
System.out.println("server is running...");
} catch (Exception e) {
e.printStackTrace();
}
started = true;
}
public boolean isStarted() {
return this.started;
}
}
还有来自Vysper的基本客户端。
import java.util.Date;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.packet.Time;
import org.jivesoftware.smackx.packet.Version;
public class BasicClient {
static class IQListener implements PacketListener {
public void processPacket(Packet packet) {
IQ iq = (IQ) packet;
String iqString = iq.toString();
System.out.println("T" + System.currentTimeMillis() + " IQ: "
+ iqString + ": " + iq.toXML());
}
}
static class PresenceListener implements PacketListener {
public void processPacket(Packet packet) {
Presence presence = (Presence) packet;
String iqString = presence.toString();
final PacketExtension extension = presence
.getExtension("http://jabber.org/protocol/caps");
if (extension != null)
System.out.println("T" + System.currentTimeMillis() + " Pres: "
+ iqString + ": " + presence.toXML());
}
}
public void test() throws XMPPException {
String me = "user2@vysper.org";
String to = "user1@vysper.org";
try {
ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration(
"localhost");
connectionConfiguration.setCompressionEnabled(false);
connectionConfiguration.setSelfSignedCertificateEnabled(true);
connectionConfiguration.setExpiredCertificatesCheckEnabled(false);
// connectionConfiguration.setDebuggerEnabled(true);
connectionConfiguration
.setSecurityMode(ConnectionConfiguration.SecurityMode.required);
// XMPPConnection.DEBUG_ENABLED = true;
XMPPConnection connection = new XMPPConnection(
connectionConfiguration);
connection.connect();
SASLAuthentication saslAuthentication = connection
.getSASLAuthentication();
saslAuthentication.authenticate(me, "password", "test");
connection.login(me, "pqssword");
connection.getRoster().setSubscriptionMode(
Roster.SubscriptionMode.accept_all);
connection.addPacketListener(new IQListener(), new PacketFilter() {
public boolean accept(Packet packet) {
return packet instanceof IQ;
}
});
connection.addPacketListener(new PresenceListener(),
new PacketFilter() {
public boolean accept(Packet packet) {
return packet instanceof Presence;
}
});
Chat chat = null;
if (to != null) {
Presence presence = new Presence(Presence.Type.subscribe);
presence.setFrom(connection.getUser());
String toEntity = to;
presence.setTo(toEntity);
connection.sendPacket(presence);
chat = connection.getChatManager().createChat(toEntity,
new MessageListener() {
public void processMessage(Chat inchat,
Message message) {
System.out.println("log received message: "
+ message.getBody());
}
});
}
connection.sendPacket(new Presence(Presence.Type.available,
"pommes", 1, Presence.Mode.available));
Thread.sleep(1000);
// query server version
sendIQGetWithTimestamp(connection, new Version());
// query server time
sendIQGetWithTimestamp(connection, new Time());
chat.sendMessage("Hello " + to + " at " + new Date());
connection.disconnect();
} catch (Throwable e) {
e.printStackTrace(); // To change body of catch statement use File |
// Settings | File Templates.
}
System.out.println("bye");
}
private static void sendIQGetWithTimestamp(XMPPConnection connection, IQ iq) {
iq.setType(IQ.Type.GET);
connection.sendPacket(iq);
System.out.println("T" + System.currentTimeMillis()
+ " IQ request sent");
}
}
我们快要准备好了。 当然,现在我们需要一个XMPP客户端,例如Pidgin 。
首先,我只想对本博客表示感谢 ,因为我不知道为什么,Vysper站点几乎没有关于如何配置Pidgin的信息,因此该博客文章非常有用。
让我向您展示我的pidgin用户的样子:
我知道,是葡萄牙语。
就是这样。 我们都准备好了。 启动您的JSF Web应用程序并播放。
注意,通信是双向的,因此您可以仅使用XMPP客户端将命令发送到服务器。 为此,您只需要更改此侦听器即可:
chat = connection.getChatManager().createChat(toEntity,
new MessageListener() {
public void processMessage(Chat inchat,
Message message) {
System.out.println("log received message: "
+ message.getBody());
}
});
我想知道是否可以创建一个DSL来处理一些命令,是否可以找到一些自动完成的pidgin插件来使用此DSL编写命令。 欢迎提出建议!
ps。 EJB不能正常关闭服务器。 但是我敢打赌,如果服务器关闭,有一些EJB注释可以做到这一点。
tomee