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 (!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 (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            if (LOG.isDebugEnabled()) {
204                LOG.debug("Detected chat server: " + chatServer);
205            }
206    
207            return room + "@" + chatServer;
208        }
209    
210        public static String getConnectionMessage(XMPPConnection connection) {
211            return connection.getHost() + ":" + connection.getPort() + "/" + connection.getServiceName();
212        }
213    
214        public String getChatId() {
215            return "Chat:" + getParticipant() + ":" + getUser();
216        }
217    
218        protected synchronized void destroy() throws Exception {
219            if (connection != null) {
220                connection.disconnect();
221            }
222        }
223    
224        // Properties
225        // -------------------------------------------------------------------------
226        public XmppBinding getBinding() {
227            if (binding == null) {
228                binding = new XmppBinding(headerFilterStrategy);
229            }
230            return binding;
231        }
232    
233        /**
234         * Sets the binding used to convert from a Camel message to and from an XMPP
235         * message
236         */
237        public void setBinding(XmppBinding binding) {
238            this.binding = binding;
239        }
240    
241        public String getHost() {
242            return host;
243        }
244    
245        public void setHost(String host) {
246            this.host = host;
247        }
248    
249        public int getPort() {
250            return port;
251        }
252    
253        public void setPort(int port) {
254            this.port = port;
255        }
256    
257        public String getUser() {
258            return user;
259        }
260    
261        public void setUser(String user) {
262            this.user = user;
263        }
264    
265        public String getPassword() {
266            return password;
267        }
268    
269        public void setPassword(String password) {
270            this.password = password;
271        }
272    
273        public String getResource() {
274            return resource;
275        }
276    
277        public void setResource(String resource) {
278            this.resource = resource;
279        }
280    
281        public boolean isLogin() {
282            return login;
283        }
284    
285        public void setLogin(boolean login) {
286            this.login = login;
287        }
288    
289        public boolean isCreateAccount() {
290            return createAccount;
291        }
292    
293        public void setCreateAccount(boolean createAccount) {
294            this.createAccount = createAccount;
295        }
296    
297        public String getRoom() {
298            return room;
299        }
300    
301        public void setRoom(String room) {
302            this.room = room;
303        }
304    
305        public String getParticipant() {
306            // participant is optional so use user if not provided
307            return participant != null ? participant : user;
308        }
309    
310        public void setParticipant(String participant) {
311            this.participant = participant;
312        }
313    
314        public String getNickname() {
315            return nickname != null ? nickname : getUser();
316        }
317    
318        public void setNickname(String nickname) {
319            this.nickname = nickname;
320        }
321    
322        public void setServiceName(String serviceName) {
323            this.serviceName = serviceName;
324        }
325    
326        public String getServiceName() {
327            return serviceName;
328        }    
329        
330        public HeaderFilterStrategy getHeaderFilterStrategy() {
331            return headerFilterStrategy;
332        }
333    
334        public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
335            this.headerFilterStrategy = headerFilterStrategy;
336        }
337    
338        // Implementation methods
339        // -------------------------------------------------------------------------
340    
341    }