KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > event > XMPPBridge


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.event;
21
22 import java.io.ByteArrayInputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.ObjectInputStream JavaDoc;
25 import java.io.ObjectOutputStream JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.Collections JavaDoc;
28
29 import org.jivesoftware.smack.GroupChat;
30 import org.jivesoftware.smack.PacketListener;
31 import org.jivesoftware.smack.SSLXMPPConnection;
32 import org.jivesoftware.smack.XMPPConnection;
33 import org.jivesoftware.smack.XMPPException;
34 import org.jivesoftware.smack.packet.Message;
35 import org.jivesoftware.smack.packet.Packet;
36 import org.apache.cayenne.CayenneRuntimeException;
37 import org.apache.cayenne.util.Base64Codec;
38 import org.apache.cayenne.util.Util;
39
40 /**
41  * An EventBridge implementation based on XMPP protocol and Smack XMPP client library.
42  * What's good about XMPP (Extensible Messaging and Presence Protocol, an IETF standard
43  * protocol that grew up from Jabber IM) is that generally it has fewer or no deployment
44  * limitations (unlike JMS and JGroups that are generally a good solution for local
45  * controlled networks). Also it provides lots of additional information for free, such as
46  * presence, and much more.
47  * <p>
48  * This implementation is based on Smack XMPP client library from JiveSoftware.
49  * </p>
50  *
51  * @since 1.2
52  * @author Andrus Adamchik
53  */

54 public class XMPPBridge extends EventBridge {
55
56     static final String JavaDoc DEFAULT_CHAT_SERVICE = "conference";
57     static final int DEFAULT_XMPP_PORT = 5222;
58     static final int DEFAULT_XMPP_SECURE_PORT = 5223;
59
60     protected boolean secureConnection;
61     protected String JavaDoc loginId;
62     protected String JavaDoc password;
63     protected String JavaDoc xmppHost;
64     protected int xmppPort;
65     protected String JavaDoc chatService;
66     protected String JavaDoc sessionHandle;
67
68     protected XMPPConnection connection;
69     protected GroupChat groupChat;
70     protected boolean connected;
71
72     /**
73      * Creates an XMPPBridge. External subject will be used as the chat group name.
74      */

75     public XMPPBridge(EventSubject localSubject, String JavaDoc externalSubject) {
76         this(Collections.singleton(localSubject), externalSubject);
77     }
78
79     /**
80      * Creates an XMPPBridge. External subject will be used as the chat group name.
81      */

82     public XMPPBridge(Collection JavaDoc localSubjects, String JavaDoc externalSubject) {
83         super(localSubjects, externalSubject);
84
85         // generate a unique session handle... users can override it to use a specific
86
// handle...
87
this.sessionHandle = "cayenne-xmpp-" + System.currentTimeMillis();
88     }
89
90     public String JavaDoc getXmppHost() {
91         return xmppHost;
92     }
93
94     public void setXmppHost(String JavaDoc xmppHost) {
95         this.xmppHost = xmppHost;
96     }
97
98     public int getXmppPort() {
99         return xmppPort;
100     }
101
102     public void setXmppPort(int xmppPort) {
103         this.xmppPort = xmppPort;
104     }
105
106     public String JavaDoc getLoginId() {
107         return loginId;
108     }
109
110     public void setLoginId(String JavaDoc loginId) {
111         this.loginId = loginId;
112     }
113
114     public String JavaDoc getPassword() {
115         return password;
116     }
117
118     public void setPassword(String JavaDoc password) {
119         this.password = password;
120     }
121
122     public boolean isSecureConnection() {
123         return secureConnection;
124     }
125
126     public void setSecureConnection(boolean secureConnection) {
127         this.secureConnection = secureConnection;
128     }
129
130     public String JavaDoc getChatService() {
131         return chatService;
132     }
133
134     public void setChatService(String JavaDoc chatService) {
135         this.chatService = chatService;
136     }
137
138     public String JavaDoc getSessionHandle() {
139         return sessionHandle;
140     }
141
142     public void setSessionHandle(String JavaDoc sessionHandle) {
143         this.sessionHandle = sessionHandle;
144     }
145
146     protected void startupExternal() throws Exception JavaDoc {
147
148         // validate settings
149
if (xmppHost == null) {
150             throw new CayenneRuntimeException("Null 'xmppHost', can't start XMPPBridge");
151         }
152
153         // shutdown old bridge
154
if (connected) {
155             shutdownExternal();
156         }
157
158         try {
159             // connect and log in to chat
160
if (secureConnection) {
161                 int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_SECURE_PORT;
162                 this.connection = new SSLXMPPConnection(xmppHost, port);
163             }
164             else {
165                 int port = xmppPort > 0 ? xmppPort : DEFAULT_XMPP_PORT;
166                 this.connection = new XMPPConnection(xmppHost, port);
167             }
168
169             if (loginId != null) {
170                 // it is important to provide a (pseudo) globally unique string as the
171
// third argument ("sessionHandle" is such string); without it same
172
// loginId can not be reused from the same machine.
173
connection.login(loginId, password, sessionHandle);
174             }
175             else {
176                 connection.loginAnonymously();
177             }
178         }
179         catch (XMPPException e) {
180             throw new CayenneRuntimeException("Error connecting to XMPP Server"
181                     + e.getLocalizedMessage());
182         }
183
184         String JavaDoc service = this.chatService != null
185                 ? this.chatService
186                 : DEFAULT_CHAT_SERVICE;
187
188         try {
189             this.groupChat = connection.createGroupChat(externalSubject
190                     + '@'
191                     + service
192                     + "."
193                     + connection.getHost());
194
195             groupChat.join(sessionHandle);
196             groupChat.addMessageListener(new XMPPListener());
197         }
198         catch (XMPPException e) {
199             throw new CayenneRuntimeException("Error setting up a group chat: "
200                     + e.getLocalizedMessage());
201         }
202
203         this.connected = true;
204     }
205
206     protected void shutdownExternal() throws Exception JavaDoc {
207         if (groupChat != null) {
208             groupChat.leave();
209             groupChat = null;
210         }
211
212         if (connection != null) {
213             connection.close();
214             connection = null;
215         }
216
217         connected = false;
218     }
219
220     protected void sendExternalEvent(CayenneEvent localEvent) throws Exception JavaDoc {
221
222         Message message = groupChat.createMessage();
223         message.setBody(serializeToString(localEvent));
224
225         // set thread to our session handle to be able to discard messages from self
226
message.setThread(sessionHandle);
227
228         groupChat.sendMessage(message);
229     }
230
231     class XMPPListener implements PacketListener {
232
233         public void processPacket(Packet packet) {
234
235             if (packet instanceof Message) {
236                 Message message = (Message) packet;
237
238                 // filter our own messages
239
if (sessionHandle.equals(message.getThread())) {
240                     // discarding
241
}
242                 else {
243
244                     String JavaDoc payload = message.getBody();
245
246                     try {
247                         Object JavaDoc event = deserializeFromString(payload);
248                         if (event instanceof CayenneEvent) {
249                             onExternalEvent((CayenneEvent) event);
250                         }
251                     }
252                     catch (Exception JavaDoc ex) {
253                         // ignore for now... need to add logging.
254
}
255                 }
256             }
257         }
258     }
259
260     /**
261      * Decodes the String (assuming it is using Base64 encoding), and then deserializes
262      * object from the byte array.
263      */

264     static Object JavaDoc deserializeFromString(String JavaDoc string) throws Exception JavaDoc {
265         if (Util.isEmptyString(string)) {
266             return null;
267         }
268
269         byte[] bytes = Base64Codec.decodeBase64(string.getBytes());
270         ObjectInputStream JavaDoc in = new ObjectInputStream JavaDoc(new ByteArrayInputStream JavaDoc(bytes));
271         Object JavaDoc object = in.readObject();
272         in.close();
273         return object;
274     }
275
276     /**
277      * Serializes object and then encodes it using Base64 encoding.
278      */

279     static String JavaDoc serializeToString(Object JavaDoc object) throws Exception JavaDoc {
280         ByteArrayOutputStream JavaDoc bytes = new ByteArrayOutputStream JavaDoc();
281         ObjectOutputStream JavaDoc out = new ObjectOutputStream JavaDoc(bytes);
282         out.writeObject(object);
283         out.close();
284
285         return new String JavaDoc(Base64Codec.encodeBase64(bytes.toByteArray()));
286     }
287 }
288
Popular Tags