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.jivesoftware.smack.AccountManager;
033 import org.jivesoftware.smack.ConnectionConfiguration;
034 import org.jivesoftware.smack.XMPPConnection;
035 import org.jivesoftware.smack.XMPPException;
036 import org.jivesoftware.smack.filter.PacketFilter;
037 import org.jivesoftware.smack.packet.Message;
038 import org.jivesoftware.smack.packet.Packet;
039 import org.jivesoftware.smackx.muc.MultiUserChat;
040 import org.slf4j.Logger;
041 import org.slf4j.LoggerFactory;
042
043 /**
044 * A XMPP Endpoint
045 *
046 * @version
047 */
048 public class XmppEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware {
049 private static final transient Logger LOG = LoggerFactory.getLogger(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 (!connection.isAuthenticated()) {
154 if (user != null) {
155 if (LOG.isDebugEnabled()) {
156 LOG.debug("Logging in to XMPP as user: {} on connection: {}", user, getConnectionMessage(connection));
157 }
158 if (password == null) {
159 LOG.warn("No password configured for user: {} on connection: {}", user, getConnectionMessage(connection));
160 }
161
162 if (createAccount) {
163 AccountManager accountManager = new AccountManager(connection);
164 accountManager.createAccount(user, password);
165 }
166 if (login) {
167 if (resource != null) {
168 connection.login(user, password, resource);
169 } else {
170 connection.login(user, password);
171 }
172 }
173 } else {
174 if (LOG.isDebugEnabled()) {
175 LOG.debug("Logging in anonymously to XMPP on connection: {}", getConnectionMessage(connection));
176 }
177 connection.loginAnonymously();
178 }
179
180 // presence is not needed to be sent after login
181 }
182
183 return connection;
184 }
185
186 /*
187 * If there is no "@" symbol in the room, find the chat service JID and
188 * return fully qualified JID for the room as room@conference.server.domain
189 */
190 public String resolveRoom(XMPPConnection connection) throws XMPPException {
191 ObjectHelper.notEmpty(room, "room");
192
193 if (room.indexOf('@', 0) != -1) {
194 return room;
195 }
196
197 Iterator<String> iterator = MultiUserChat.getServiceNames(connection).iterator();
198 if (!iterator.hasNext()) {
199 throw new XMPPException("Cannot find Multi User Chat service on connection: " + getConnectionMessage(connection));
200 }
201
202 String chatServer = iterator.next();
203 LOG.debug("Detected chat server: {}", chatServer);
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 // Properties
217 // -------------------------------------------------------------------------
218 public XmppBinding getBinding() {
219 if (binding == null) {
220 binding = new XmppBinding(headerFilterStrategy);
221 }
222 return binding;
223 }
224
225 /**
226 * Sets the binding used to convert from a Camel message to and from an XMPP
227 * message
228 */
229 public void setBinding(XmppBinding binding) {
230 this.binding = binding;
231 }
232
233 public String getHost() {
234 return host;
235 }
236
237 public void setHost(String host) {
238 this.host = host;
239 }
240
241 public int getPort() {
242 return port;
243 }
244
245 public void setPort(int port) {
246 this.port = port;
247 }
248
249 public String getUser() {
250 return user;
251 }
252
253 public void setUser(String user) {
254 this.user = user;
255 }
256
257 public String getPassword() {
258 return password;
259 }
260
261 public void setPassword(String password) {
262 this.password = password;
263 }
264
265 public String getResource() {
266 return resource;
267 }
268
269 public void setResource(String resource) {
270 this.resource = resource;
271 }
272
273 public boolean isLogin() {
274 return login;
275 }
276
277 public void setLogin(boolean login) {
278 this.login = login;
279 }
280
281 public boolean isCreateAccount() {
282 return createAccount;
283 }
284
285 public void setCreateAccount(boolean createAccount) {
286 this.createAccount = createAccount;
287 }
288
289 public String getRoom() {
290 return room;
291 }
292
293 public void setRoom(String room) {
294 this.room = room;
295 }
296
297 public String getParticipant() {
298 // participant is optional so use user if not provided
299 return participant != null ? participant : user;
300 }
301
302 public void setParticipant(String participant) {
303 this.participant = participant;
304 }
305
306 public String getNickname() {
307 return nickname != null ? nickname : getUser();
308 }
309
310 public void setNickname(String nickname) {
311 this.nickname = nickname;
312 }
313
314 public void setServiceName(String serviceName) {
315 this.serviceName = serviceName;
316 }
317
318 public String getServiceName() {
319 return serviceName;
320 }
321
322 public HeaderFilterStrategy getHeaderFilterStrategy() {
323 return headerFilterStrategy;
324 }
325
326 public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
327 this.headerFilterStrategy = headerFilterStrategy;
328 }
329
330 // Implementation methods
331 // -------------------------------------------------------------------------
332
333 @Override
334 protected void doStop() throws Exception {
335 if (connection != null) {
336 connection.disconnect();
337 }
338 connection = null;
339 binding = null;
340 }
341
342 }