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 org.apache.camel.Exchange;
020    import org.apache.camel.Processor;
021    import org.apache.camel.impl.DefaultConsumer;
022    import org.jivesoftware.smack.Chat;
023    import org.jivesoftware.smack.ChatManager;
024    import org.jivesoftware.smack.ChatManagerListener;
025    import org.jivesoftware.smack.MessageListener;
026    import org.jivesoftware.smack.PacketListener;
027    import org.jivesoftware.smack.SmackConfiguration;
028    import org.jivesoftware.smack.XMPPConnection;
029    import org.jivesoftware.smack.filter.AndFilter;
030    import org.jivesoftware.smack.filter.PacketTypeFilter;
031    import org.jivesoftware.smack.filter.ToContainsFilter;
032    import org.jivesoftware.smack.packet.Message;
033    import org.jivesoftware.smack.packet.Packet;
034    import org.jivesoftware.smack.packet.Presence;
035    import org.jivesoftware.smackx.muc.DiscussionHistory;
036    import org.jivesoftware.smackx.muc.MultiUserChat;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    /**
041     * A {@link org.apache.camel.Consumer Consumer} which listens to XMPP packets
042     *
043     * @version 
044     */
045    public class XmppConsumer extends DefaultConsumer implements PacketListener, MessageListener, ChatManagerListener {
046        private static final transient Logger LOG = LoggerFactory.getLogger(XmppConsumer.class);
047        private final XmppEndpoint endpoint;
048        private MultiUserChat muc;
049        private Chat privateChat;
050        private ChatManager chatManager;
051        private XMPPConnection connection;
052    
053        public XmppConsumer(XmppEndpoint endpoint, Processor processor) {
054            super(endpoint, processor);
055            this.endpoint = endpoint;
056        }
057    
058        @Override
059        protected void doStart() throws Exception {
060            connection = endpoint.createConnection();
061            chatManager = connection.getChatManager();
062            chatManager.addChatListener(this);
063    
064            if (endpoint.getRoom() == null) {
065                privateChat = chatManager.getThreadChat(endpoint.getChatId());
066    
067                if (privateChat != null) {
068                    if (LOG.isDebugEnabled()) {
069                        LOG.debug("Adding listener to existing chat opened to " + privateChat.getParticipant());
070                    }
071                    privateChat.addMessageListener(this);
072                } else {                
073                    privateChat = connection.getChatManager().createChat(endpoint.getParticipant(), endpoint.getChatId(), this);
074                    if (LOG.isDebugEnabled()) {
075                        LOG.debug("Opening private chat to " + privateChat.getParticipant());
076                    }
077                }
078            } else {
079                // add the presence packet listener to the connection so we only get packets that concerns us
080                // we must add the listener before creating the muc
081                final ToContainsFilter toFilter = new ToContainsFilter(endpoint.getParticipant());
082                final AndFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), toFilter);
083                connection.addPacketListener(this, packetFilter);
084    
085                muc = new MultiUserChat(connection, endpoint.resolveRoom(connection));
086                muc.addMessageListener(this);
087                DiscussionHistory history = new DiscussionHistory();
088                history.setMaxChars(0); // we do not want any historical messages
089    
090                muc.join(endpoint.getNickname(), null, history, SmackConfiguration.getPacketReplyTimeout());
091                if (LOG.isInfoEnabled()) {
092                    LOG.info("Joined room: {} as: {}", muc.getRoom(), endpoint.getNickname());
093                }
094            }
095    
096            super.doStart();
097        }
098    
099        @Override
100        protected void doStop() throws Exception {
101            super.doStop();
102            if (muc != null) {
103                if (LOG.isInfoEnabled()) {
104                    LOG.info("Leaving room: {}", muc.getRoom());
105                }
106                muc.removeMessageListener(this);
107                muc.leave();
108                muc = null;
109            }
110            if (connection != null && connection.isConnected()) {
111                connection.disconnect();
112            }
113        }
114    
115        public void chatCreated(Chat chat, boolean createdLocally) {
116            if (!createdLocally) {
117                if (LOG.isDebugEnabled()) {
118                    LOG.debug("Accepting incoming chat session from " + chat.getParticipant());
119                }
120                chat.addMessageListener(this);
121            }
122        }
123    
124        public void processPacket(Packet packet) {
125            if (packet instanceof Message) {
126                processMessage(null, (Message)packet);
127            }
128        }
129    
130        public void processMessage(Chat chat, Message message) {
131            if (LOG.isDebugEnabled()) {
132                LOG.debug("Received XMPP message for {} from {} : {}", new Object[]{endpoint.getUser(), endpoint.getParticipant(), message.getBody()});
133            }
134    
135            Exchange exchange = endpoint.createExchange(message);
136            try {
137                getProcessor().process(exchange);
138            } catch (Exception e) {
139                exchange.setException(e);
140            } finally {
141                // must remove message from muc to avoid messages stacking up and causing OutOfMemoryError
142                // pollMessage is a non blocking method
143                // (see http://issues.igniterealtime.org/browse/SMACK-129)
144                if (muc != null) {
145                    muc.pollMessage();
146                }
147            }
148        }
149    }