001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.component.xmpp;
018
019 import java.util.Iterator;
020
021 import org.apache.camel.Consumer;
022 import org.apache.camel.Exchange;
023 import org.apache.camel.ExchangePattern;
024 import org.apache.camel.Processor;
025 import org.apache.camel.Producer;
026 import org.apache.camel.impl.DefaultEndpoint;
027 import org.apache.camel.impl.DefaultExchange;
028 import org.apache.camel.impl.DefaultHeaderFilterStrategy;
029 import org.apache.camel.spi.HeaderFilterStrategy;
030 import org.apache.camel.spi.HeaderFilterStrategyAware;
031 import org.apache.camel.util.ObjectHelper;
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034 import org.jivesoftware.smack.AccountManager;
035 import org.jivesoftware.smack.ConnectionConfiguration;
036 import org.jivesoftware.smack.XMPPConnection;
037 import org.jivesoftware.smack.XMPPException;
038 import org.jivesoftware.smack.filter.PacketFilter;
039 import org.jivesoftware.smack.packet.Message;
040 import org.jivesoftware.smack.packet.Packet;
041 import org.jivesoftware.smackx.muc.MultiUserChat;
042
043 /**
044 * A XMPP Endpoint
045 *
046 * @version $Revision:520964 $
047 */
048 public class XmppEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware {
049 private static final transient Log LOG = LogFactory.getLog(XmppEndpoint.class);
050 private HeaderFilterStrategy headerFilterStrategy = new DefaultHeaderFilterStrategy();
051 private XmppBinding binding;
052 private String host;
053 private int port;
054 private String user;
055 private String password;
056 private String resource = "Camel";
057 private boolean login = true;
058 private boolean createAccount;
059 private String room;
060 private String participant;
061 private String nickname;
062 private String serviceName;
063 private XMPPConnection connection;
064
065 public XmppEndpoint() {
066 }
067
068 public XmppEndpoint(String uri, XmppComponent component) {
069 super(uri, component);
070 }
071
072 public XmppEndpoint(String endpointUri) {
073 super(endpointUri);
074 }
075
076 public Producer createProducer() throws Exception {
077 if (room != null) {
078 return createGroupChatProducer();
079 } else {
080 if (getParticipant() == null) {
081 throw new IllegalArgumentException("No room or participant configured on this endpoint: " + this);
082 }
083 return createPrivateChatProducer(getParticipant());
084 }
085 }
086
087 public Producer createGroupChatProducer() throws Exception {
088 return new XmppGroupChatProducer(this);
089 }
090
091 public Producer createPrivateChatProducer(String participant) throws Exception {
092 return new XmppPrivateChatProducer(this, participant);
093 }
094
095 public Consumer createConsumer(Processor processor) throws Exception {
096 return new XmppConsumer(this, processor);
097 }
098
099 @Override
100 public Exchange createExchange(ExchangePattern pattern) {
101 return createExchange(pattern, null);
102 }
103
104 public Exchange createExchange(Message message) {
105 return createExchange(getExchangePattern(), message);
106 }
107
108 private Exchange createExchange(ExchangePattern pattern, Message message) {
109 Exchange exchange = new DefaultExchange(this, getExchangePattern());
110 exchange.setProperty(Exchange.BINDING, getBinding());
111 exchange.setIn(new XmppMessage(message));
112 return exchange;
113 }
114
115 @Override
116 protected String createEndpointUri() {
117 return "xmpp://" + host + ":" + port + "/" + getParticipant() + "?serviceName=" + serviceName;
118 }
119
120 public boolean isSingleton() {
121 return true;
122 }
123
124 public XMPPConnection createConnection() throws XMPPException {
125
126 if (connection != null) {
127 return connection;
128 }
129
130 if (port > 0) {
131 if (getServiceName() == null) {
132 connection = new XMPPConnection(new ConnectionConfiguration(host, port));
133 } else {
134 connection = new XMPPConnection(new ConnectionConfiguration(host, port, serviceName));
135 }
136 } else {
137 connection = new XMPPConnection(host);
138 }
139
140 connection.connect();
141
142 connection.addPacketListener(new XmppLogger("INBOUND"), new PacketFilter() {
143 public boolean accept(Packet packet) {
144 return true;
145 }
146 });
147 connection.addPacketWriterListener(new XmppLogger("OUTBOUND"), new PacketFilter() {
148 public boolean accept(Packet packet) {
149 return true;
150 }
151 });
152
153 if (login && !connection.isAuthenticated()) {
154 if (user != null) {
155 if (LOG.isDebugEnabled()) {
156 LOG.debug("Logging in to XMPP as user: " + user + " on connection: " + getConnectionMessage(connection));
157 }
158 if (password == null) {
159 LOG.warn("No password configured for user: " + user + " on connection: " + getConnectionMessage(connection));
160 }
161
162 if (createAccount) {
163 AccountManager accountManager = new AccountManager(connection);
164 accountManager.createAccount(user, password);
165 }
166 if (resource != null) {
167 connection.login(user, password, resource);
168 } else {
169 connection.login(user, password);
170 }
171 } else {
172 if (LOG.isDebugEnabled()) {
173 LOG.debug("Logging in anonymously to XMPP on connection: " + getConnectionMessage(connection));
174 }
175 connection.loginAnonymously();
176 }
177
178 // presence is not needed to be sent after login
179 }
180
181 return connection;
182 }
183
184 /*
185 * If there is no "@" symbol in the room, find the chat service JID and
186 * return fully qualified JID for the room as room@conference.server.domain
187 */
188 public String resolveRoom(XMPPConnection connection) throws XMPPException {
189 ObjectHelper.notEmpty(room, "room");
190
191 if (room.indexOf('@', 0) != -1) {
192 return room;
193 }
194
195 Iterator<String> iterator = MultiUserChat.getServiceNames(connection).iterator();
196 if (!iterator.hasNext()) {
197 throw new XMPPException("Cannot find Multi User Chat service on connection: " + getConnectionMessage(connection));
198 }
199
200 String chatServer = iterator.next();
201 if (LOG.isDebugEnabled()) {
202 LOG.debug("Detected chat server: " + chatServer);
203 }
204
205 return room + "@" + chatServer;
206 }
207
208 public static String getConnectionMessage(XMPPConnection connection) {
209 return connection.getHost() + ":" + connection.getPort() + "/" + connection.getServiceName();
210 }
211
212 public String getChatId() {
213 return "Chat:" + getParticipant() + ":" + getUser();
214 }
215
216 protected synchronized void destroy() throws Exception {
217 if (connection != null) {
218 connection.disconnect();
219 }
220 }
221
222 // Properties
223 // -------------------------------------------------------------------------
224 public XmppBinding getBinding() {
225 if (binding == null) {
226 binding = new XmppBinding(headerFilterStrategy);
227 }
228 return binding;
229 }
230
231 /**
232 * Sets the binding used to convert from a Camel message to and from an XMPP
233 * message
234 */
235 public void setBinding(XmppBinding binding) {
236 this.binding = binding;
237 }
238
239 public String getHost() {
240 return host;
241 }
242
243 public void setHost(String host) {
244 this.host = host;
245 }
246
247 public int getPort() {
248 return port;
249 }
250
251 public void setPort(int port) {
252 this.port = port;
253 }
254
255 public String getUser() {
256 return user;
257 }
258
259 public void setUser(String user) {
260 this.user = user;
261 }
262
263 public String getPassword() {
264 return password;
265 }
266
267 public void setPassword(String password) {
268 this.password = password;
269 }
270
271 public String getResource() {
272 return resource;
273 }
274
275 public void setResource(String resource) {
276 this.resource = resource;
277 }
278
279 public boolean isLogin() {
280 return login;
281 }
282
283 public void setLogin(boolean login) {
284 this.login = login;
285 }
286
287 public boolean isCreateAccount() {
288 return createAccount;
289 }
290
291 public void setCreateAccount(boolean createAccount) {
292 this.createAccount = createAccount;
293 }
294
295 public String getRoom() {
296 return room;
297 }
298
299 public void setRoom(String room) {
300 this.room = room;
301 }
302
303 public String getParticipant() {
304 // participant is optional so use user if not provided
305 return participant != null ? participant : user;
306 }
307
308 public void setParticipant(String participant) {
309 this.participant = participant;
310 }
311
312 public String getNickname() {
313 return nickname != null ? nickname : getUser();
314 }
315
316 public void setNickname(String nickname) {
317 this.nickname = nickname;
318 }
319
320 public void setServiceName(String serviceName) {
321 this.serviceName = serviceName;
322 }
323
324 public String getServiceName() {
325 return serviceName;
326 }
327
328 public HeaderFilterStrategy getHeaderFilterStrategy() {
329 return headerFilterStrategy;
330 }
331
332 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
333 this.headerFilterStrategy = headerFilterStrategy;
334 }
335
336 // Implementation methods
337 // -------------------------------------------------------------------------
338
339 }