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    }