1 /**
2 * Copyright 2005-2006 the original author or authors.
3 *
4 * Licensed under the Gnu General Pubic License, Version 2.0 (the
5 * "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/gpl-license.php
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the Gnu General Public License for more details.
14 */
15 package org.figure8.join.services.notification.im;
16
17 import org.figure8.join.core.ParameterDefinition;
18 import org.figure8.join.core.InvalidParameterException;
19 import org.figure8.join.services.notification.SubscribersNotifier;
20 import org.figure8.join.services.notification.NotificationException;
21 import org.figure8.join.services.remoting.beans.RemoteSubscriber;
22 import org.figure8.join.util.LogUtil;
23
24 import org.apache.commons.logging.Log;
25 import org.jivesoftware.smack.GroupChat;
26 import org.jivesoftware.smack.XMPPException;
27 import org.jivesoftware.smack.XMPPConnection;
28 import org.jivesoftware.smack.Chat;
29 import org.jivesoftware.smack.packet.Message;
30
31 import java.util.List;
32 import java.util.Iterator;
33 import java.util.ArrayList;
34 import java.util.Properties;
35 /**
36 * This is an implementation of {@link SubscribersNotifier} using the Instant
37 * Messenger as notification media. This implementation uses the Jabber/XMPP
38 * protocol for message sending. It uses the Smack libray for that (see
39 * http://www.jivesoftware.org/smack for details)
40 * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
41 * @version $Revision: 1.1 $
42 */
43 public class XMPPNotifier implements SubscribersNotifier{
44
45
46
47 /** Get a commons logger. */
48 private static final Log log = LogUtil.getLog(XMPPNotifier.class);
49
50 /** Name of configurable parameter denoting the port of IM server. */
51 public static final String PORT_PARAM = "port";
52 /** Name of configurable parameter denoting the name of IM server. */
53 public static final String SERVER_PARAM = "server";
54 /** Name of configurable parameter denoting the user name for connecting to IM. */
55 public static final String USER_PARAM = "username";
56 /** Name of configurable parameter denoting the password for connecting to IM. */
57 public static final String PASSWORD_PARAM = "password";
58 /** Name of configurable parameter denoting the chat room where to send message. */
59 public static final String ROOM_PARAM = "chatRoom";
60 /** Name of configurable parameter denoting the nick name for joining chat room. */
61 public static final String PSEUDO_PARAM = "pseudo";
62
63 /** List of {@link ParameterDefinition}s supported by this notifier */
64 protected static List parameters = new ArrayList();
65
66 /** ParameterDefinition representation of parameter denoting the port of the IM server. */
67 protected static final ParameterDefinition portParam = new ParameterDefinition(PORT_PARAM,
68 "The port of the IM server to connect to (blanck means default)", "5222", false);
69 /** ParameterDefinition representation of parameter denoting the name of the IM server. */
70 protected static final ParameterDefinition serverParam = new ParameterDefinition(SERVER_PARAM,
71 "The name of the IM server (Service, DNS name of IP address)", "jabber.org", true);
72 /** ParameterDefinition representation of parameter denoting the user name for connecting to IM. */
73 protected static final ParameterDefinition userParam = new ParameterDefinition(USER_PARAM,
74 "The user for connecting to IM server", "jsmith@jabber.org", true);
75 /** ParameterDefinition representation of parameter denoting the password for connecting to IM. */
76 protected static final ParameterDefinition passwordParam = new ParameterDefinition(PASSWORD_PARAM,
77 "The password for connection to IM server", "mypassword", true);
78 /** ParameterDefintion representation of parameter denoting the chat room where to send messages. */
79 protected static final ParameterDefinition roomParam = new ParameterDefinition(ROOM_PARAM,
80 "The chat room or group where to send messages", "test@jabber.org", false);
81 /** ParameterDefintion representation of parameter denoting the nick name for joining chat room. */
82 protected static final ParameterDefinition pseudoParam = new ParameterDefinition(PSEUDO_PARAM,
83 "The nick name used for joining chat room or group", "jsmith", false);
84
85 static{
86 parameters.add(portParam);
87 parameters.add(serverParam);
88 parameters.add(userParam);
89 parameters.add(passwordParam);
90 parameters.add(roomParam);
91 parameters.add(pseudoParam);
92 }
93
94
95
96
97 /** The port of IM server to connect to */
98 private int port = -1;
99 /** The name of IM server to connect to */
100 private String server;
101 /** The username for connecting to IM server */
102 private String username;
103 /** The password for connecting to IM server */
104 private String password;
105 /** The name of chat room to send messages to (if group publishing) */
106 private String chatRoom;
107 /** The pseudo to use for joining chat room (if group publishing) */
108 private String pseudo;
109
110
111
112
113 /** Creates a new instance of XMPPNotifier */
114 public XMPPNotifier(){
115 }
116
117 /**
118 * Creates a new instance of XMPPNotifier with mandatory attributes for connecting to IM
119 * @param server The name of IM server to connect to
120 * @param port The port of IM server to connect to
121 * @param username The username for connecting to IM server
122 * @param password The password for connecting to IM server
123 */
124 public XMPPNotifier(String server, int port, String username, String password){
125 this.server = server;
126 this.port = port;
127 this.username = username;
128 this.password = password;
129 }
130
131
132
133
134 /** @return The port of IM server to connect to */
135 public int getPort(){
136 return port;
137 }
138 /** @param port The port of IM server to connect to */
139 public void setPort(int port){
140 this.port = port;
141 }
142 /** @return The name of IM server to connect to */
143 public String getServer(){
144 return server;
145 }
146 /** @param server The name of IM server to connect to */
147 public void setServer(String server){
148 this.server = server;
149 }
150 /** @return The username for connecting to IM server */
151 public String getUsername(){
152 return username;
153 }
154 /** @parma username The username for connecting to IM server */
155 public void setUsername(String username){
156 this.username = username;
157 }
158 /** @return The password for connecting to IM server */
159 public String getPassword(){
160 return password;
161 }
162 /** @param password The password for connecting to IM server */
163 public void setPassword(String password){
164 this.password = password;
165 }
166 /** @return The name of chat room to send messages to (if group publishing) */
167 public String getChatRoom(){
168 return chatRoom;
169 }
170 /** @param chatRoom The name of chat room to send messages to (if group publishing) */
171 public void setChatRoom(String chatRoom){
172 this.chatRoom = chatRoom;
173 }
174 /** @return The pseudo to use for joining chat room (if group publishing) */
175 public String getPseudo(){
176 return pseudo;
177 }
178 /** @param pseudo The pseudo to use for joining chat room (if group publishing) */
179 public void setPseudo(String pseudo){
180 this.pseudo = pseudo;
181 }
182
183
184
185
186 /**
187 * Extract mail addresses for subscribers to provide recipients. Use mail address
188 * for the moment : RemoteSubscriber do not have an IM identifier
189 * @param subscribers The subscribers to get recipients for
190 * @return The list of corresponding recipients.
191 */
192 public String[] extractRecipients(RemoteSubscriber[] subscribers){
193 String[] recipients = new String[subscribers.length];
194 for (int i=0; i<subscribers.length; i++){
195 recipients[i] = subscribers[i].getMailAddress();
196 }
197 return recipients;
198 }
199
200 /**
201 * Notify subscribers of mailing list defined into Join application
202 * @param subscribers The subscriber of mailing list to notify
203 * @param title The title of notification message to send
204 * @param content The content of notification message to send
205 * @throws org.figure8.join.services.notification.NotificationException if an exception occurs during the notification operation
206 * (usually they are network related exceptions)
207 */
208 public void notify(RemoteSubscriber[] subscribers, String title, String content) throws NotificationException{
209
210 notify(extractRecipients(subscribers), title, content);
211 }
212
213 /**
214 * Notifier are able to notify recipients and use a title and a content within
215 * their notification messages. The definition of a recipients is voluntary abstract
216 * letting implementations or sub-interfaces defining what they consider as valid
217 * recipients (ex: mail addresses, IM identifies, etc...)
218 * @param recipients An array of recipients to notify. Recipient is described with a String
219 * @param title The title of notification message to send
220 * @param content The content of notification message to send
221 * @throws NotificationException if an exception occurs during the notification operation
222 * (usually they are network related exceptions)
223 */
224 public void notify(String[] recipients, String title, String content) throws NotificationException{
225 if (log.isDebugEnabled())
226 log.debug("Notifying recipients using " + server + " with " + username + "/" + password + " credentials");
227
228 XMPPConnection con = connect();
229
230 try{
231
232 if (chatRoom != null && chatRoom.length() > 0){
233 if (log.isDebugEnabled())
234 log.debug("Sending a message to the chat room " + chatRoom);
235 GroupChat chat = con.createGroupChat(chatRoom);
236 chat.join(pseudo);
237 Message msg = chat.createMessage();
238 msg.setSubject(title);
239 msg.setBody(content);
240 chat.sendMessage(msg);
241 chat.leave();
242 }
243 else{
244
245 for (int i=0; i<recipients.length; i++){
246 if (log.isDebugEnabled())
247 log.debug("Sending a message to recipient " + recipients[i]);
248 try{
249 Chat chat = con.createChat(recipients[i]);
250 Message msg = chat.createMessage();
251 msg.setSubject(title);
252 msg.setBody(content);
253 chat.sendMessage(msg);
254 }
255 catch (XMPPException xe){
256
257 log.warn("Exception while sending message to recipient " + recipients[i]);
258 log.warn("Here's the detailed message: " + xe.getMessage());
259 }
260 }
261 }
262 }
263 catch (XMPPException xe){
264
265 if (xe.getXMPPError().getCode() == 409)
266 log.error("Exception while joining chat room. Used pseudo (" + pseudo + ") seems already used");
267
268 log.error("Here's the detailed message: " + xe.getMessage());
269 throw new NotificationException("Exception while sending messages to XMPP server", xe);
270 }
271 finally{
272
273 try {con.close();}
274 catch (Exception e) {
275 }
276 }
277
278
279
280
281 /**
282 * Get this object parameters definitions as a list
283 * @return A list of {@code ParameterDefinition} objects
284 */
285 public List getParameterDefinitionsAsList(){
286 return parameters;
287 }
288
289 /**
290 * Get this object parameters definitions as an array
291 * @return An array of {@code ParameterDefinition} objects
292 */
293 public ParameterDefinition[] getParameterDefinitions(){
294 List result = getParameterDefinitionsAsList();
295 return (ParameterDefinition[])result.toArray(new ParameterDefinition[result.size()]);
296 }
297
298 /**
299 * Set the value of a parameter using its nama
300 * @param parameterName The name of parameter so set value for
301 * @param parameterValue The value of the paramater
302 * @throws InvalidParameterException if this parameter is not supported by this object
303 */
304 public void setParameter(String parameterName, String parameterValue) throws InvalidParameterException{
305 if (PORT_PARAM.equals(parameterName)){
306 try {setPort(Integer.parseInt(parameterValue));}
307 catch (Exception e){
308
309 log.error("Port parameter is not an integer !");
310 throw new InvalidParameterException("Port parameter is not an integer");
311 }
312 }
313 else if (SERVER_PARAM.equals(parameterName))
314 setServer(parameterValue);
315 else if (USER_PARAM.equals(parameterName))
316 setUsername(parameterValue);
317 else if (PASSWORD_PARAM.equals(parameterName))
318 setPassword(parameterValue);
319 else if (ROOM_PARAM.equals(parameterName))
320 setChatRoom(parameterValue);
321 else if (PSEUDO_PARAM.equals(parameterName))
322 setPseudo(parameterValue);
323 }
324
325 /**
326 * Set the value of a parameter using its definitions
327 * @param parameter The definitino of the paramater to set
328 * @param parameterValue The value of the parameter
329 * @throws InvalidParameterException if this parameter is not supported by this object
330 */
331 public void setParameter(ParameterDefinition parameter, String parameterValue) throws InvalidParameterException{
332 setParameter(parameter.getName(), parameterValue);
333 }
334
335 /**
336 * Convenient methods for setting all attributes values using a single method.
337 * @param parameters Properties where keys are parameter names
338 * @throws InvalidParameterException if one of these parameters is not supported by this object
339 */
340 public void setParameters(Properties parameters) throws InvalidParameterException{
341 Iterator keys = parameters.keySet().iterator();
342 while (keys != null && keys.hasNext()){
343 String parameterName = (String)keys.next();
344 String parameterValue = parameters.getProperty(parameterName);
345 setParameter(parameterName, parameterValue);
346 }
347 }
348
349
350
351
352 /**
353 * Create a new connection and log in.
354 * @throws NotificationException if an exception occurs while creating exception.
355 * @return A new XMPPConnection with user logged in
356 */
357 protected XMPPConnection connect() throws NotificationException{
358
359 XMPPConnection con = null;
360 try{
361 con = new XMPPConnection(server, port);
362 con.login(username, password);
363 return con;
364 }
365 catch (XMPPException xe){
366
367 log.error("Exception while connecting to the XMPP server " + server + ": " + xe.getMessage());
368 throw new NotificationException("Exception while connecting to the XMPP server", xe);
369 }
370 }
371 }