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    }