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                    LOG.debug("Adding listener to existing chat opened to " + privateChat.getParticipant());
069                    privateChat.addMessageListener(this);
070                } else {                
071                    privateChat = connection.getChatManager().createChat(endpoint.getParticipant(), endpoint.getChatId(), this);
072                    LOG.debug("Opening private chat to " + privateChat.getParticipant());
073                }
074            } else {
075                // add the presence packet listener to the connection so we only get packets that concerns us
076                // we must add the listener before creating the muc
077                final ToContainsFilter toFilter = new ToContainsFilter(endpoint.getParticipant());
078                final AndFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class), toFilter);
079                connection.addPacketListener(this, packetFilter);
080    
081                muc = new MultiUserChat(connection, endpoint.resolveRoom(connection));
082                muc.addMessageListener(this);
083                DiscussionHistory history = new DiscussionHistory();
084                history.setMaxChars(0); // we do not want any historical messages
085    
086                muc.join(endpoint.getNickname(), null, history, SmackConfiguration.getPacketReplyTimeout());
087                if (LOG.isInfoEnabled()) {
088                    LOG.info("Joined room: " + muc.getRoom() + " as: " + endpoint.getNickname());
089                }
090            }
091    
092            super.doStart();
093        }
094    
095        @Override
096        protected void doStop() throws Exception {
097            super.doStop();
098            if (muc != null) {
099                if (LOG.isInfoEnabled()) {
100                    LOG.info("Leaving room: " + muc.getRoom());
101                }
102                muc.removeMessageListener(this);
103                muc.leave();
104                muc = null;
105            }
106            if (connection != null && connection.isConnected()) {
107                connection.disconnect();
108            }
109        }
110    
111        public void chatCreated(Chat chat, boolean createdLocally) {
112            if (!createdLocally) {
113                LOG.debug("Accepting incoming chat session from " + chat.getParticipant());
114                chat.addMessageListener(this);
115            }
116        }
117    
118        public void processPacket(Packet packet) {
119            if (packet instanceof Message) {
120                processMessage(null, (Message)packet);
121            }
122        }
123    
124        public void processMessage(Chat chat, Message message) {
125            if (LOG.isDebugEnabled()) {
126                LOG.debug("Received XMPP message for " + endpoint.getUser() + " from " + endpoint.getParticipant() + " : " + message.getBody());
127            }
128    
129            Exchange exchange = endpoint.createExchange(message);
130            try {
131                getProcessor().process(exchange);
132            } catch (Exception e) {
133                exchange.setException(e);
134            } finally {
135                // must remove message from muc to avoid messages stacking up and causing OutOfMemoryError
136                // pollMessage is a non blocking method
137                // (see http://issues.igniterealtime.org/browse/SMACK-129)
138                if (muc != null) {
139                    muc.pollMessage();
140                }
141            }
142        }
143    }