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.DefaultHeaderFilterStrategy;
028    import org.apache.camel.spi.HeaderFilterStrategy;
029    import org.apache.camel.spi.HeaderFilterStrategyAware;
030    import org.apache.camel.util.ObjectHelper;
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.jivesoftware.smack.AccountManager;
034    import org.jivesoftware.smack.ConnectionConfiguration;
035    import org.jivesoftware.smack.XMPPConnection;
036    import org.jivesoftware.smack.XMPPException;
037    import org.jivesoftware.smack.packet.Message;
038    import org.jivesoftware.smackx.muc.MultiUserChat;
039    
040    /**
041     * A XMPP Endpoint
042     *
043     * @version $Revision:520964 $
044     */
045    public class XmppEndpoint extends DefaultEndpoint implements HeaderFilterStrategyAware {
046        private static final transient Log LOG = LogFactory.getLog(XmppEndpoint.class);
047        private HeaderFilterStrategy headerFilterStrategy = new DefaultHeaderFilterStrategy();
048        private XmppBinding binding;
049        private String host;
050        private int port;
051        private String user;
052        private String password;
053        private String resource = "Camel";
054        private boolean login = true;
055        private boolean createAccount;
056        private String room;
057        private String participant;
058        private String nickname;
059        private String serviceName;
060    
061        public XmppEndpoint() {
062        }
063    
064        public XmppEndpoint(String uri, XmppComponent component) {
065            super(uri, component);
066        }
067    
068        public XmppEndpoint(String endpointUri) {
069            super(endpointUri);
070        }
071    
072        public Producer createProducer() throws Exception {
073            if (room != null) {
074                return createGroupChatProducer();
075            } else {
076                if (getParticipant() == null) {
077                    throw new IllegalArgumentException("No room or participant configured on this endpoint: " + this);
078                }
079                return createPrivateChatProducer(getParticipant());
080            }
081        }
082    
083        public Producer createGroupChatProducer() throws Exception {
084            return new XmppGroupChatProducer(this);
085        }
086    
087        public Producer createPrivateChatProducer(String participant) throws Exception {
088            return new XmppPrivateChatProducer(this, participant);
089        }
090    
091        public Consumer createConsumer(Processor processor) throws Exception {
092            return new XmppConsumer(this, processor);
093        }
094    
095        @Override
096        public Exchange createExchange(ExchangePattern pattern) {
097            return new XmppExchange(this, pattern, getBinding());
098        }
099    
100        public XmppExchange createExchange(Message message) {
101            return new XmppExchange(this, getExchangePattern(), getBinding(), message);
102        }
103    
104        @Override
105        protected String createEndpointUri() {
106            return "xmpp://" + host + ":" + port + "/" + getParticipant() + "?serviceName=" + serviceName;
107        }
108    
109        public boolean isSingleton() {
110            return true;
111        }
112        
113        public XMPPConnection createConnection() throws XMPPException {
114            XMPPConnection connection;
115    
116            if (port > 0) {
117                if (getServiceName() == null) {
118                    connection = new XMPPConnection(new ConnectionConfiguration(host, port));
119                } else {
120                    connection = new XMPPConnection(new ConnectionConfiguration(host, port, getServiceName()));
121                }
122            } else {
123                connection = new XMPPConnection(host);
124            }
125    
126            connection.connect();
127    
128            if (login && !connection.isAuthenticated()) {
129                if (user != null) {
130                    if (LOG.isDebugEnabled()) {
131                        LOG.debug("Logging in to XMPP as user: " + user + " on connection: " + getConnectionMessage(connection));
132                    }
133                    if (password == null) {
134                        LOG.warn("No password configured for user: " + user + " on connection: " + getConnectionMessage(connection));
135                    }
136    
137                    if (createAccount) {
138                        AccountManager accountManager = new AccountManager(connection);
139                        accountManager.createAccount(user, password);
140                    }
141                    if (resource != null) {
142                        connection.login(user, password, resource);
143                    } else {
144                        connection.login(user, password);
145                    }
146                } else {
147                    if (LOG.isDebugEnabled()) {
148                        LOG.debug("Logging in anonymously to XMPP on connection: "  + getConnectionMessage(connection));
149                    }
150                    connection.loginAnonymously();
151                }
152    
153                // presence is not needed to be sent after login
154            }
155    
156            return connection;
157        }
158    
159        /*
160         * If there is no "@" symbol in the room, find the chat service JID and
161         * return fully qualified JID for the room as room@conference.server.domain
162         */
163        public String resolveRoom(XMPPConnection connection) throws XMPPException {
164            ObjectHelper.notEmpty(room, "room");
165    
166            if (room.indexOf('@', 0) != -1) {
167                return room;
168            }
169    
170            Iterator<String> iterator = MultiUserChat.getServiceNames(connection).iterator();
171            if (!iterator.hasNext()) {
172                throw new XMPPException("Cannot find Multi User Chat service on connection: " + getConnectionMessage(connection));
173            }
174    
175            String chatServer = iterator.next();
176            if (LOG.isDebugEnabled()) {
177                LOG.debug("Detected chat server: " + chatServer);
178            }
179    
180            return room + "@" + chatServer;
181        }
182    
183        public static String getConnectionMessage(XMPPConnection connetion) {
184            return connetion.getHost() + ":" + connetion.getPort() + "/" + connetion.getServiceName();
185        }
186    
187        // Properties
188        // -------------------------------------------------------------------------
189        public XmppBinding getBinding() {
190            if (binding == null) {
191                binding = new XmppBinding(headerFilterStrategy);
192            }
193            return binding;
194        }
195    
196        /**
197         * Sets the binding used to convert from a Camel message to and from an XMPP
198         * message
199         */
200        public void setBinding(XmppBinding binding) {
201            this.binding = binding;
202        }
203    
204        public String getHost() {
205            return host;
206        }
207    
208        public void setHost(String host) {
209            this.host = host;
210        }
211    
212        public int getPort() {
213            return port;
214        }
215    
216        public void setPort(int port) {
217            this.port = port;
218        }
219    
220        public String getUser() {
221            return user;
222        }
223    
224        public void setUser(String user) {
225            this.user = user;
226        }
227    
228        public String getPassword() {
229            return password;
230        }
231    
232        public void setPassword(String password) {
233            this.password = password;
234        }
235    
236        public String getResource() {
237            return resource;
238        }
239    
240        public void setResource(String resource) {
241            this.resource = resource;
242        }
243    
244        public boolean isLogin() {
245            return login;
246        }
247    
248        public void setLogin(boolean login) {
249            this.login = login;
250        }
251    
252        public boolean isCreateAccount() {
253            return createAccount;
254        }
255    
256        public void setCreateAccount(boolean createAccount) {
257            this.createAccount = createAccount;
258        }
259    
260        public String getRoom() {
261            return room;
262        }
263    
264        public void setRoom(String room) {
265            this.room = room;
266        }
267    
268        public String getParticipant() {
269            // participant is optional so use user if not provided
270            return participant != null ? participant : user;
271        }
272    
273        public void setParticipant(String participant) {
274            this.participant = participant;
275        }
276    
277        public String getNickname() {
278            return nickname != null ? nickname : getUser();
279        }
280    
281        public void setNickname(String nickname) {
282            this.nickname = nickname;
283        }
284    
285        public void setServiceName(String serviceName) {
286            this.serviceName = serviceName;
287        }
288    
289        public String getServiceName() {
290            return serviceName;
291        }    
292        
293        public HeaderFilterStrategy getHeaderFilterStrategy() {
294            return headerFilterStrategy;
295        }
296    
297        public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
298            this.headerFilterStrategy = headerFilterStrategy;
299        }
300    
301        // Implementation methods
302        // -------------------------------------------------------------------------
303    
304    }