001/**
002 *
003 * Copyright 2009 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 */
017package org.jivesoftware.smack;
018
019import java.io.IOException;
020import java.io.Reader;
021import java.io.Writer;
022import java.lang.reflect.Constructor;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Map;
026import java.util.Set;
027import java.util.concurrent.ConcurrentHashMap;
028import java.util.concurrent.ConcurrentLinkedQueue;
029import java.util.concurrent.CopyOnWriteArrayList;
030import java.util.concurrent.CopyOnWriteArraySet;
031import java.util.concurrent.ScheduledExecutorService;
032import java.util.concurrent.ScheduledFuture;
033import java.util.concurrent.ScheduledThreadPoolExecutor;
034import java.util.concurrent.ThreadFactory;
035import java.util.concurrent.TimeUnit;
036import java.util.concurrent.atomic.AtomicBoolean;
037import java.util.concurrent.atomic.AtomicInteger;
038import java.util.logging.Level;
039import java.util.logging.Logger;
040
041import javax.security.sasl.SaslException;
042
043import org.jivesoftware.smack.SmackException.NoResponseException;
044import org.jivesoftware.smack.SmackException.NotConnectedException;
045import org.jivesoftware.smack.SmackException.ConnectionException;
046import org.jivesoftware.smack.SmackException.ResourceBindingNotOfferedException;
047import org.jivesoftware.smack.XMPPException.XMPPErrorException;
048import org.jivesoftware.smack.compression.XMPPInputOutputStream;
049import org.jivesoftware.smack.debugger.SmackDebugger;
050import org.jivesoftware.smack.filter.IQReplyFilter;
051import org.jivesoftware.smack.filter.PacketFilter;
052import org.jivesoftware.smack.packet.Bind;
053import org.jivesoftware.smack.packet.IQ;
054import org.jivesoftware.smack.packet.Packet;
055import org.jivesoftware.smack.packet.Presence;
056import org.jivesoftware.smack.packet.Session;
057
058/**
059 * The abstract XMPPConnection class provides an interface for connections to a XMPP server and
060 * implements shared methods which are used by the different types of connections (e.g.
061 * {@link XMPPTCPConnection} or {@link XMPPBOSHConnection}). To create a connection to a XMPP server
062 * a simple usage of this API might look like the following:
063 * <p>
064 * 
065 * <pre>
066 * // Create a connection to the igniterealtime.org XMPP server.
067 * XMPPConnection con = new XMPPTCPConnection("igniterealtime.org");
068 * // Connect to the server
069 * con.connect();
070 * // Most servers require you to login before performing other tasks.
071 * con.login("jsmith", "mypass");
072 * // Start a new conversation with John Doe and send him a message.
073 * Chat chat = ChatManager.getInstanceFor(con).createChat(<font color="green">"jdoe@igniterealtime.org"</font>, new MessageListener() {
074 *     public void processMessage(Chat chat, Message message) {
075 *         // Print out any messages we get back to standard out.
076 *         System.out.println(<font color="green">"Received message: "</font> + message);
077 *     }
078 * });
079 * chat.sendMessage(<font color="green">"Howdy!"</font>);
080 * // Disconnect from the server
081 * con.disconnect();
082 * </pre>
083 * <p>
084 * Connections can be reused between connections. This means that an Connection may be connected,
085 * disconnected and then connected again. Listeners of the Connection will be retained accross
086 * connections.
087 * <p>
088 * If a connected XMPPConnection gets disconnected abruptly and automatic reconnection is enabled (
089 * {@link ConnectionConfiguration#isReconnectionAllowed()}, the default), then it will try to
090 * reconnect again. To stop the reconnection process, use {@link #disconnect()}. Once stopped you
091 * can use {@link #connect()} to manually connect to the server.
092 * 
093 * @author Matt Tucker
094 * @author Guenther Niess
095 */
096@SuppressWarnings("javadoc")
097public abstract class XMPPConnection {
098    private static final Logger LOGGER = Logger.getLogger(XMPPConnection.class.getName());
099    
100    /** 
101     * Counter to uniquely identify connections that are created.
102     */
103    private final static AtomicInteger connectionCounter = new AtomicInteger(0);
104
105    /**
106     * A set of listeners which will be invoked if a new connection is created.
107     */
108    private final static Set<ConnectionCreationListener> connectionEstablishedListeners =
109            new CopyOnWriteArraySet<ConnectionCreationListener>();
110
111    static {
112        // Ensure the SmackConfiguration class is loaded by calling a method in it.
113        SmackConfiguration.getVersion();
114    }
115
116    /**
117     * A collection of ConnectionListeners which listen for connection closing
118     * and reconnection events.
119     */
120    protected final Collection<ConnectionListener> connectionListeners =
121            new CopyOnWriteArrayList<ConnectionListener>();
122
123    /**
124     * A collection of PacketCollectors which collects packets for a specified filter
125     * and perform blocking and polling operations on the result queue.
126     */
127    protected final Collection<PacketCollector> collectors = new ConcurrentLinkedQueue<PacketCollector>();
128
129    /**
130     * List of PacketListeners that will be notified when a new packet was received.
131     */
132    protected final Map<PacketListener, ListenerWrapper> recvListeners =
133            new ConcurrentHashMap<PacketListener, ListenerWrapper>();
134
135    /**
136     * List of PacketListeners that will be notified when a new packet was sent.
137     */
138    protected final Map<PacketListener, ListenerWrapper> sendListeners =
139            new ConcurrentHashMap<PacketListener, ListenerWrapper>();
140
141    /**
142     * List of PacketInterceptors that will be notified when a new packet is about to be
143     * sent to the server. These interceptors may modify the packet before it is being
144     * actually sent to the server.
145     */
146    protected final Map<PacketInterceptor, InterceptorWrapper> interceptors =
147            new ConcurrentHashMap<PacketInterceptor, InterceptorWrapper>();
148
149    /**
150     * 
151     */
152    private long packetReplyTimeout = SmackConfiguration.getDefaultPacketReplyTimeout();
153
154    /**
155     * The SmackDebugger allows to log and debug XML traffic.
156     */
157    protected SmackDebugger debugger = null;
158
159    /**
160     * The Reader which is used for the debugger.
161     */
162    protected Reader reader;
163
164    /**
165     * The Writer which is used for the debugger.
166     */
167    protected Writer writer;
168
169
170    /**
171     * The SASLAuthentication manager that is responsible for authenticating with the server.
172     */
173    protected SASLAuthentication saslAuthentication = new SASLAuthentication(this);
174
175    /**
176     * A number to uniquely identify connections that are created. This is distinct from the
177     * connection ID, which is a value sent by the server once a connection is made.
178     */
179    protected final int connectionCounterValue = connectionCounter.getAndIncrement();
180
181    /**
182     * Holds the initial configuration used while creating the connection.
183     */
184    protected final ConnectionConfiguration config;
185
186    /**
187     * Holds the Caps Node information for the used XMPP service (i.e. the XMPP server)
188     */
189    private String serviceCapsNode;
190
191    /**
192     * Defines how the from attribute of outgoing stanzas should be handled.
193     */
194    private FromMode fromMode = FromMode.OMITTED;
195
196    /**
197     * Stores whether the server supports rosterVersioning
198     */
199    private boolean rosterVersioningSupported = false;
200
201    protected XMPPInputOutputStream compressionHandler;
202
203    /**
204     * ExecutorService used to invoke the PacketListeners on newly arrived and parsed stanzas. It is
205     * important that we use a <b>single threaded ExecutorService</b> in order to guarantee that the
206     * PacketListeners are invoked in the same order the stanzas arrived.
207     */
208    private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
209                    new SmackExecutorThreadFactory(connectionCounterValue));
210
211    /**
212     * SmackExecutorThreadFactory is a *static* inner class of XMPPConnection. Note that we must not
213     * use anonymous classes in order to prevent threads from leaking.
214     */
215    private static final class SmackExecutorThreadFactory implements ThreadFactory {
216        private final int connectionCounterValue;
217        private int count = 0;
218
219        private SmackExecutorThreadFactory(int connectionCounterValue) {
220            this.connectionCounterValue = connectionCounterValue;
221        }
222
223        @Override
224        public Thread newThread(Runnable runnable) {
225            Thread thread = new Thread(runnable, "Smack Executor Service " + count++ + " ("
226                            + connectionCounterValue + ")");
227            thread.setDaemon(true);
228            return thread;
229        }
230    }
231
232    private Roster roster;
233
234    /**
235     * The used host to establish the connection to
236     */
237    private String host;
238
239    /**
240     * The used port to establish the connection to
241     */
242    private int port;
243
244    /**
245     * Set to true if the server requires the connection to be binded in order to continue.
246     * <p>
247     * Note that we use AtomicBoolean here because it allows us to set the Boolean *object*, which
248     * we also use as synchronization object. A plain non-atomic Boolean object would be newly created
249     * for every change of the boolean value, which makes it useless as object for wait()/notify().
250     */
251    private AtomicBoolean bindingRequired = new AtomicBoolean(false);
252
253    private boolean sessionSupported;
254
255    /**
256     * 
257     */
258    private IOException connectionException;
259
260    /**
261     * Flag that indicates if the user is currently authenticated with the server.
262     */
263    protected boolean authenticated = false;
264
265    /**
266     * Flag that indicates if the user was authenticated with the server when the connection
267     * to the server was closed (abruptly or not).
268     */
269    protected boolean wasAuthenticated = false;
270
271    /**
272     * Create a new XMPPConnection to a XMPP server.
273     * 
274     * @param configuration The configuration which is used to establish the connection.
275     */
276    protected XMPPConnection(ConnectionConfiguration configuration) {
277        config = configuration;
278    }
279
280    /**
281     * Returns the configuration used to connect to the server.
282     * 
283     * @return the configuration used to connect to the server.
284     */
285    protected ConnectionConfiguration getConfiguration() {
286        return config;
287    }
288
289    /**
290     * Returns the name of the service provided by the XMPP server for this connection.
291     * This is also called XMPP domain of the connected server. After
292     * authenticating with the server the returned value may be different.
293     * 
294     * @return the name of the service provided by the XMPP server.
295     */
296    public String getServiceName() {
297        return config.getServiceName();
298    }
299
300    /**
301     * Returns the host name of the server where the XMPP server is running. This would be the
302     * IP address of the server or a name that may be resolved by a DNS server.
303     * 
304     * @return the host name of the server where the XMPP server is running or null if not yet connected.
305     */
306    public String getHost() {
307        return host;
308    }
309
310    /**
311     * Returns the port number of the XMPP server for this connection. The default port
312     * for normal connections is 5222.
313     * 
314     * @return the port number of the XMPP server or 0 if not yet connected.
315     */
316    public int getPort() {
317        return port;
318    }
319
320    /**
321     * Returns the full XMPP address of the user that is logged in to the connection or
322     * <tt>null</tt> if not logged in yet. An XMPP address is in the form
323     * username@server/resource.
324     * 
325     * @return the full XMPP address of the user logged in.
326     */
327    public abstract String getUser();
328
329    /**
330     * Returns the connection ID for this connection, which is the value set by the server
331     * when opening a XMPP stream. If the server does not set a connection ID, this value
332     * will be null. This value will be <tt>null</tt> if not connected to the server.
333     * 
334     * @return the ID of this connection returned from the XMPP server or <tt>null</tt> if
335     *      not connected to the server.
336     */
337    public abstract String getConnectionID();
338
339    /**
340     * Returns true if currently connected to the XMPP server.
341     * 
342     * @return true if connected.
343     */
344    public abstract boolean isConnected();
345
346    /**
347     * Returns true if currently authenticated by successfully calling the login method.
348     * 
349     * @return true if authenticated.
350     */
351    public abstract boolean isAuthenticated();
352
353    /**
354     * Returns true if currently authenticated anonymously.
355     * 
356     * @return true if authenticated anonymously.
357     */
358    public abstract boolean isAnonymous();
359
360    /**
361     * Returns true if the connection to the server has successfully negotiated encryption. 
362     * 
363     * @return true if a secure connection to the server.
364     */
365    public abstract boolean isSecureConnection();
366
367    protected abstract void sendPacketInternal(Packet packet) throws NotConnectedException;
368
369    /**
370     * Returns true if network traffic is being compressed. When using stream compression network
371     * traffic can be reduced up to 90%. Therefore, stream compression is ideal when using a slow
372     * speed network connection. However, the server will need to use more CPU time in order to
373     * un/compress network data so under high load the server performance might be affected.
374     * 
375     * @return true if network traffic is being compressed.
376     */
377    public abstract boolean isUsingCompression();
378
379    /**
380     * Establishes a connection to the XMPP server and performs an automatic login
381     * only if the previous connection state was logged (authenticated). It basically
382     * creates and maintains a connection to the server.
383     * <p>
384     * Listeners will be preserved from a previous connection.
385     * 
386     * @throws XMPPException if an error occurs on the XMPP protocol level.
387     * @throws SmackException if an error occurs somewhere else besides XMPP protocol level.
388     * @throws IOException 
389     * @throws ConnectionException with detailed information about the failed connection.
390     */
391    public void connect() throws SmackException, IOException, XMPPException {
392        saslAuthentication.init();
393        bindingRequired.set(false);
394        sessionSupported = false;
395        connectionException = null;
396        connectInternal();
397    }
398
399    /**
400     * Abstract method that concrete subclasses of XMPPConnection need to implement to perform their
401     * way of XMPP connection establishment. Implementations must guarantee that this method will
402     * block until the last features stanzas has been parsed and the features have been reported
403     * back to XMPPConnection (e.g. by calling @{link {@link XMPPConnection#serverRequiresBinding()}
404     * and such).
405     * <p>
406     * Also implementations are required to perform an automatic login if the previous connection
407     * state was logged (authenticated).
408     *
409     * @throws SmackException
410     * @throws IOException
411     * @throws XMPPException
412     */
413    protected abstract void connectInternal() throws SmackException, IOException, XMPPException;
414
415    /**
416     * Logs in to the server using the strongest authentication mode supported by
417     * the server, then sets presence to available. If the server supports SASL authentication 
418     * then the user will be authenticated using SASL if not Non-SASL authentication will 
419     * be tried. If more than five seconds (default timeout) elapses in each step of the 
420     * authentication process without a response from the server, or if an error occurs, a 
421     * XMPPException will be thrown.<p>
422     * 
423     * Before logging in (i.e. authenticate) to the server the connection must be connected.
424     * 
425     * It is possible to log in without sending an initial available presence by using
426     * {@link ConnectionConfiguration#setSendPresence(boolean)}. If this connection is
427     * not interested in loading its roster upon login then use
428     * {@link ConnectionConfiguration#setRosterLoadedAtLogin(boolean)}.
429     * Finally, if you want to not pass a password and instead use a more advanced mechanism
430     * while using SASL then you may be interested in using
431     * {@link ConnectionConfiguration#setCallbackHandler(javax.security.auth.callback.CallbackHandler)}.
432     * For more advanced login settings see {@link ConnectionConfiguration}.
433     * 
434     * @param username the username.
435     * @param password the password or <tt>null</tt> if using a CallbackHandler.
436     * @throws XMPPException if an error occurs on the XMPP protocol level.
437     * @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
438     * @throws IOException 
439     * @throws SaslException 
440     */
441    public void login(String username, String password) throws XMPPException, SmackException, SaslException, IOException {
442        login(username, password, "Smack");
443    }
444
445    /**
446     * Logs in to the server using the strongest authentication mode supported by
447     * the server, then sets presence to available. If the server supports SASL authentication 
448     * then the user will be authenticated using SASL if not Non-SASL authentication will 
449     * be tried. If more than five seconds (default timeout) elapses in each step of the 
450     * authentication process without a response from the server, or if an error occurs, a 
451     * XMPPException will be thrown.<p>
452     * 
453     * Before logging in (i.e. authenticate) to the server the connection must be connected.
454     * 
455     * It is possible to log in without sending an initial available presence by using
456     * {@link ConnectionConfiguration#setSendPresence(boolean)}. If this connection is
457     * not interested in loading its roster upon login then use
458     * {@link ConnectionConfiguration#setRosterLoadedAtLogin(boolean)}.
459     * Finally, if you want to not pass a password and instead use a more advanced mechanism
460     * while using SASL then you may be interested in using
461     * {@link ConnectionConfiguration#setCallbackHandler(javax.security.auth.callback.CallbackHandler)}.
462     * For more advanced login settings see {@link ConnectionConfiguration}.
463     * 
464     * @param username the username.
465     * @param password the password or <tt>null</tt> if using a CallbackHandler.
466     * @param resource the resource.
467     * @throws XMPPException if an error occurs on the XMPP protocol level.
468     * @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
469     * @throws IOException 
470     * @throws SaslException 
471     */
472    public abstract void login(String username, String password, String resource) throws XMPPException, SmackException, SaslException, IOException;
473
474    /**
475     * Logs in to the server anonymously. Very few servers are configured to support anonymous
476     * authentication, so it's fairly likely logging in anonymously will fail. If anonymous login
477     * does succeed, your XMPP address will likely be in the form "123ABC@server/789XYZ" or
478     * "server/123ABC" (where "123ABC" and "789XYZ" is a random value generated by the server).
479     * 
480     * @throws XMPPException if an error occurs on the XMPP protocol level.
481     * @throws SmackException if an error occurs somehwere else besides XMPP protocol level.
482     * @throws IOException 
483     * @throws SaslException 
484     */
485    public abstract void loginAnonymously() throws XMPPException, SmackException, SaslException, IOException;
486
487    /**
488     * Notification message saying that the server requires the client to bind a
489     * resource to the stream.
490     */
491    protected void serverRequiresBinding() {
492        synchronized (bindingRequired) {
493            bindingRequired.set(true);
494            bindingRequired.notify();
495        }
496    }
497
498    /**
499     * Notification message saying that the server supports sessions. When a server supports
500     * sessions the client needs to send a Session packet after successfully binding a resource
501     * for the session.
502     */
503    protected void serverSupportsSession() {
504        sessionSupported = true;
505    }
506
507    protected String bindResourceAndEstablishSession(String resource) throws XMPPErrorException,
508                    ResourceBindingNotOfferedException, NoResponseException, NotConnectedException {
509
510        synchronized (bindingRequired) {
511            if (!bindingRequired.get()) {
512                try {
513                    bindingRequired.wait(getPacketReplyTimeout());
514                }
515                catch (InterruptedException e) {
516                    // Ignore
517                }
518                if (!bindingRequired.get()) {
519                    // Server never offered resource binding, which is REQURIED in XMPP client and
520                    // server
521                    // implementations as per RFC6120 7.2
522                    throw new ResourceBindingNotOfferedException();
523                }
524            }
525        }
526
527        Bind bindResource = new Bind();
528        bindResource.setResource(resource);
529
530        Bind response = (Bind) createPacketCollectorAndSend(bindResource).nextResultOrThrow();
531        String userJID = response.getJid();
532
533        if (sessionSupported && !getConfiguration().isLegacySessionDisabled()) {
534            Session session = new Session();
535            createPacketCollectorAndSend(session).nextResultOrThrow();
536        }
537        return userJID;
538    }
539
540    protected void setConnectionException(IOException exception) {
541        connectionException = exception;
542    }
543
544    protected void throwConnectionExceptionOrNoResponse() throws IOException, NoResponseException {
545        if (connectionException != null) {
546            throw connectionException;
547        } else {
548            throw new NoResponseException();
549        }
550    }
551
552    protected Reader getReader() {
553        return reader;
554    }
555
556    protected Writer getWriter() {
557        return writer;
558    }
559
560    protected void setServiceName(String serviceName) {
561        config.setServiceName(serviceName);
562    }
563    
564    protected void setLoginInfo(String username, String password, String resource) {
565        config.setLoginInfo(username, password, resource);
566    }
567    
568    protected void serverSupportsAccountCreation() {
569        AccountManager.getInstance(this).setSupportsAccountCreation(true);
570    }
571
572    protected void maybeResolveDns() throws Exception {
573        config.maybeResolveDns();
574    }
575
576    /**
577     * Sends the specified packet to the server.
578     * 
579     * @param packet the packet to send.
580     * @throws NotConnectedException 
581     */
582    public void sendPacket(Packet packet) throws NotConnectedException {
583        if (!isConnected()) {
584            throw new NotConnectedException();
585        }
586        if (packet == null) {
587            throw new NullPointerException("Packet is null.");
588        }
589        switch (fromMode) {
590        case OMITTED:
591            packet.setFrom(null);
592            break;
593        case USER:
594            packet.setFrom(getUser());
595            break;
596        case UNCHANGED:
597        default:
598            break;
599        }
600        // Invoke interceptors for the new packet that is about to be sent. Interceptors may modify
601        // the content of the packet.
602        firePacketInterceptors(packet);
603        sendPacketInternal(packet);
604        // Process packet writer listeners. Note that we're using the sending thread so it's
605        // expected that listeners are fast.
606        firePacketSendingListeners(packet);
607    }
608
609    /**
610     * Returns the roster for the user.
611     * <p>
612     * This method will never return <code>null</code>, instead if the user has not yet logged into
613     * the server or is logged in anonymously all modifying methods of the returned roster object
614     * like {@link Roster#createEntry(String, String, String[])},
615     * {@link Roster#removeEntry(RosterEntry)} , etc. except adding or removing
616     * {@link RosterListener}s will throw an IllegalStateException.
617     * 
618     * @return the user's roster.
619     * @throws IllegalStateException if the connection is anonymous
620     */
621    public Roster getRoster() {
622        if (isAnonymous()) {
623            throw new IllegalStateException("Anonymous users can't have a roster");
624        }
625        // synchronize against login()
626        synchronized(this) {
627            if (roster == null) {
628                roster = new Roster(this);
629            }
630            if (!isAuthenticated()) {
631                return roster;
632            }
633        }
634
635        // If this is the first time the user has asked for the roster after calling
636        // login, we want to wait for the server to send back the user's roster. This
637        // behavior shields API users from having to worry about the fact that roster
638        // operations are asynchronous, although they'll still have to listen for
639        // changes to the roster. Note: because of this waiting logic, internal
640        // Smack code should be wary about calling the getRoster method, and may need to
641        // access the roster object directly.
642        // Also only check for rosterInitalized is isRosterLoadedAtLogin is set, otherwise the user
643        // has to manually call Roster.reload() before he can expect a initialized roster.
644        if (!roster.rosterInitialized && config.isRosterLoadedAtLogin()) {
645            try {
646                synchronized (roster) {
647                    long waitTime = getPacketReplyTimeout();
648                    long start = System.currentTimeMillis();
649                    while (!roster.rosterInitialized) {
650                        if (waitTime <= 0) {
651                            break;
652                        }
653                        roster.wait(waitTime);
654                        long now = System.currentTimeMillis();
655                        waitTime -= now - start;
656                        start = now;
657                    }
658                }
659            }
660            catch (InterruptedException ie) {
661                // Ignore.
662            }
663        }
664        return roster;
665    }
666
667    /**
668     * Returns the SASLAuthentication manager that is responsible for authenticating with
669     * the server.
670     * 
671     * @return the SASLAuthentication manager that is responsible for authenticating with
672     *         the server.
673     */
674    protected SASLAuthentication getSASLAuthentication() {
675        return saslAuthentication;
676    }
677
678    /**
679     * Closes the connection by setting presence to unavailable then closing the connection to
680     * the XMPP server. The XMPPConnection can still be used for connecting to the server
681     * again.
682     *
683     * @throws NotConnectedException 
684     */
685    public void disconnect() throws NotConnectedException {
686        disconnect(new Presence(Presence.Type.unavailable));
687    }
688
689    /**
690     * Closes the connection. A custom unavailable presence is sent to the server, followed
691     * by closing the stream. The XMPPConnection can still be used for connecting to the server
692     * again. A custom unavailable presence is useful for communicating offline presence
693     * information such as "On vacation". Typically, just the status text of the presence
694     * packet is set with online information, but most XMPP servers will deliver the full
695     * presence packet with whatever data is set.
696     * 
697     * @param unavailablePresence the presence packet to send during shutdown.
698     * @throws NotConnectedException 
699     */
700    public synchronized void disconnect(Presence unavailablePresence) throws NotConnectedException {
701        if (!isConnected()) {
702            return;
703        }
704
705        sendPacket(unavailablePresence);
706        shutdown();
707        callConnectionClosedListener();
708    };
709
710    /**
711     * Shuts the current connection down.
712     */
713    protected abstract void shutdown();
714
715    /**
716     * Adds a new listener that will be notified when new Connections are created. Note
717     * that newly created connections will not be actually connected to the server.
718     * 
719     * @param connectionCreationListener a listener interested on new connections.
720     */
721    public static void addConnectionCreationListener(
722            ConnectionCreationListener connectionCreationListener) {
723        connectionEstablishedListeners.add(connectionCreationListener);
724    }
725
726    /**
727     * Removes a listener that was interested in connection creation events.
728     * 
729     * @param connectionCreationListener a listener interested on new connections.
730     */
731    public static void removeConnectionCreationListener(
732            ConnectionCreationListener connectionCreationListener) {
733        connectionEstablishedListeners.remove(connectionCreationListener);
734    }
735
736    /**
737     * Get the collection of listeners that are interested in connection creation events.
738     * 
739     * @return a collection of listeners interested on new connections.
740     */
741    protected static Collection<ConnectionCreationListener> getConnectionCreationListeners() {
742        return Collections.unmodifiableCollection(connectionEstablishedListeners);
743    }
744
745    /**
746     * Adds a connection listener to this connection that will be notified when
747     * the connection closes or fails.
748     * 
749     * @param connectionListener a connection listener.
750     */
751    public void addConnectionListener(ConnectionListener connectionListener) {
752        if (connectionListener == null) {
753            return;
754        }
755        if (!connectionListeners.contains(connectionListener)) {
756            connectionListeners.add(connectionListener);
757        }
758    }
759
760    /**
761     * Removes a connection listener from this connection.
762     * 
763     * @param connectionListener a connection listener.
764     */
765    public void removeConnectionListener(ConnectionListener connectionListener) {
766        connectionListeners.remove(connectionListener);
767    }
768
769    /**
770     * Get the collection of listeners that are interested in connection events.
771     * 
772     * @return a collection of listeners interested on connection events.
773     */
774    protected Collection<ConnectionListener> getConnectionListeners() {
775        return connectionListeners;
776    }
777
778    /**
779     * Creates a new packet collector collecting packets that are replies to <code>packet</code>.
780     * Does also send <code>packet</code>. The packet filter for the collector is an
781     * {@link IQReplyFilter}, guaranteeing that packet id and JID in the 'from' address have
782     * expected values.
783     *
784     * @param packet the packet to filter responses from
785     * @return a new packet collector.
786     * @throws NotConnectedException 
787     */
788    public PacketCollector createPacketCollectorAndSend(IQ packet) throws NotConnectedException {
789        PacketFilter packetFilter = new IQReplyFilter(packet, this);
790        // Create the packet collector before sending the packet
791        PacketCollector packetCollector = createPacketCollector(packetFilter);
792        // Now we can send the packet as the collector has been created
793        sendPacket(packet);
794        return packetCollector;
795    }
796
797    /**
798     * Creates a new packet collector for this connection. A packet filter determines
799     * which packets will be accumulated by the collector. A PacketCollector is
800     * more suitable to use than a {@link PacketListener} when you need to wait for
801     * a specific result.
802     * 
803     * @param packetFilter the packet filter to use.
804     * @return a new packet collector.
805     */
806    public PacketCollector createPacketCollector(PacketFilter packetFilter) {
807        PacketCollector collector = new PacketCollector(this, packetFilter);
808        // Add the collector to the list of active collectors.
809        collectors.add(collector);
810        return collector;
811    }
812
813    /**
814     * Remove a packet collector of this connection.
815     * 
816     * @param collector a packet collectors which was created for this connection.
817     */
818    protected void removePacketCollector(PacketCollector collector) {
819        collectors.remove(collector);
820    }
821
822    /**
823     * Get the collection of all packet collectors for this connection.
824     * 
825     * @return a collection of packet collectors for this connection.
826     */
827    protected Collection<PacketCollector> getPacketCollectors() {
828        return collectors;
829    }
830
831    /**
832     * Registers a packet listener with this connection. A packet listener will be invoked only
833     * when an incoming packet is received. A packet filter determines
834     * which packets will be delivered to the listener. If the same packet listener
835     * is added again with a different filter, only the new filter will be used.
836     * 
837     * <p>
838     * NOTE: If you want get a similar callback for outgoing packets, see {@link #addPacketInterceptor(PacketInterceptor, PacketFilter)}.
839     * 
840     * @param packetListener the packet listener to notify of new received packets.
841     * @param packetFilter   the packet filter to use.
842     */
843    public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) {
844        if (packetListener == null) {
845            throw new NullPointerException("Packet listener is null.");
846        }
847        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
848        recvListeners.put(packetListener, wrapper);
849    }
850
851    /**
852     * Removes a packet listener for received packets from this connection.
853     * 
854     * @param packetListener the packet listener to remove.
855     */
856    public void removePacketListener(PacketListener packetListener) {
857        recvListeners.remove(packetListener);
858    }
859
860    /**
861     * Get a map of all packet listeners for received packets of this connection.
862     * 
863     * @return a map of all packet listeners for received packets.
864     */
865    protected Map<PacketListener, ListenerWrapper> getPacketListeners() {
866        return recvListeners;
867    }
868
869    /**
870     * Registers a packet listener with this connection. The listener will be
871     * notified of every packet that this connection sends. A packet filter determines
872     * which packets will be delivered to the listener. Note that the thread
873     * that writes packets will be used to invoke the listeners. Therefore, each
874     * packet listener should complete all operations quickly or use a different
875     * thread for processing.
876     * 
877     * @param packetListener the packet listener to notify of sent packets.
878     * @param packetFilter   the packet filter to use.
879     */
880    public void addPacketSendingListener(PacketListener packetListener, PacketFilter packetFilter) {
881        if (packetListener == null) {
882            throw new NullPointerException("Packet listener is null.");
883        }
884        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
885        sendListeners.put(packetListener, wrapper);
886    }
887
888    /**
889     * Removes a packet listener for sending packets from this connection.
890     * 
891     * @param packetListener the packet listener to remove.
892     */
893    public void removePacketSendingListener(PacketListener packetListener) {
894        sendListeners.remove(packetListener);
895    }
896
897    /**
898     * Get a map of all packet listeners for sending packets of this connection.
899     * 
900     * @return a map of all packet listeners for sent packets.
901     */
902    protected Map<PacketListener, ListenerWrapper> getPacketSendingListeners() {
903        return sendListeners;
904    }
905
906
907    /**
908     * Process all packet listeners for sending packets.
909     * 
910     * @param packet the packet to process.
911     */
912    private void firePacketSendingListeners(Packet packet) {
913        // Notify the listeners of the new sent packet
914        for (ListenerWrapper listenerWrapper : sendListeners.values()) {
915            try {
916                listenerWrapper.notifyListener(packet);
917            }
918            catch (NotConnectedException e) {
919                LOGGER.log(Level.WARNING, "Got not connected exception, aborting");
920                break;
921            }
922        }
923    }
924
925    /**
926     * Registers a packet interceptor with this connection. The interceptor will be
927     * invoked every time a packet is about to be sent by this connection. Interceptors
928     * may modify the packet to be sent. A packet filter determines which packets
929     * will be delivered to the interceptor.
930     * 
931     * <p>
932     * NOTE: For a similar functionality on incoming packets, see {@link #addPacketListener(PacketListener, PacketFilter)}.
933     *
934     * @param packetInterceptor the packet interceptor to notify of packets about to be sent.
935     * @param packetFilter      the packet filter to use.
936     */
937    public void addPacketInterceptor(PacketInterceptor packetInterceptor,
938            PacketFilter packetFilter) {
939        if (packetInterceptor == null) {
940            throw new NullPointerException("Packet interceptor is null.");
941        }
942        interceptors.put(packetInterceptor, new InterceptorWrapper(packetInterceptor, packetFilter));
943    }
944
945    /**
946     * Removes a packet interceptor.
947     *
948     * @param packetInterceptor the packet interceptor to remove.
949     */
950    public void removePacketInterceptor(PacketInterceptor packetInterceptor) {
951        interceptors.remove(packetInterceptor);
952    }
953
954    /**
955     * Get a map of all packet interceptors for sending packets of this connection.
956     * 
957     * @return a map of all packet interceptors for sending packets.
958     */
959    protected Map<PacketInterceptor, InterceptorWrapper> getPacketInterceptors() {
960        return interceptors;
961    }
962
963    /**
964     * Process interceptors. Interceptors may modify the packet that is about to be sent.
965     * Since the thread that requested to send the packet will invoke all interceptors, it
966     * is important that interceptors perform their work as soon as possible so that the
967     * thread does not remain blocked for a long period.
968     * 
969     * @param packet the packet that is going to be sent to the server
970     */
971    private void firePacketInterceptors(Packet packet) {
972        if (packet != null) {
973            for (InterceptorWrapper interceptorWrapper : interceptors.values()) {
974                interceptorWrapper.notifyListener(packet);
975            }
976        }
977    }
978
979    /**
980     * Initialize the {@link #debugger}. You can specify a customized {@link SmackDebugger}
981     * by setup the system property <code>smack.debuggerClass</code> to the implementation.
982     * 
983     * @throws IllegalStateException if the reader or writer isn't yet initialized.
984     * @throws IllegalArgumentException if the SmackDebugger can't be loaded.
985     */
986    protected void initDebugger() {
987        if (reader == null || writer == null) {
988            throw new NullPointerException("Reader or writer isn't initialized.");
989        }
990        // If debugging is enabled, we open a window and write out all network traffic.
991        if (config.isDebuggerEnabled()) {
992            if (debugger == null) {
993                // Detect the debugger class to use.
994                String className = null;
995                // Use try block since we may not have permission to get a system
996                // property (for example, when an applet).
997                try {
998                    className = System.getProperty("smack.debuggerClass");
999                }
1000                catch (Throwable t) {
1001                    // Ignore.
1002                }
1003                Class<?> debuggerClass = null;
1004                if (className != null) {
1005                    try {
1006                        debuggerClass = Class.forName(className);
1007                    }
1008                    catch (Exception e) {
1009                        LOGGER.warning("Unabled to instantiate debugger class " + className);
1010                    }
1011                }
1012                if (debuggerClass == null) {
1013                    try {
1014                        debuggerClass =
1015                                Class.forName("org.jivesoftware.smackx.debugger.EnhancedDebugger");
1016                    }
1017                    catch (Exception ex) {
1018                        try {
1019                            debuggerClass =
1020                                    Class.forName("org.jivesoftware.smack.debugger.LiteDebugger");
1021                        }
1022                        catch (Exception ex2) {
1023                            LOGGER.warning("Unabled to instantiate either Smack debugger class");
1024                        }
1025                    }
1026                }
1027                // Create a new debugger instance. If an exception occurs then disable the debugging
1028                // option
1029                try {
1030                    Constructor<?> constructor = debuggerClass
1031                            .getConstructor(XMPPConnection.class, Writer.class, Reader.class);
1032                    debugger = (SmackDebugger) constructor.newInstance(this, writer, reader);
1033                    reader = debugger.getReader();
1034                    writer = debugger.getWriter();
1035                }
1036                catch (Exception e) {
1037                    throw new IllegalArgumentException("Can't initialize the configured debugger!", e);
1038                }
1039            }
1040            else {
1041                // Obtain new reader and writer from the existing debugger
1042                reader = debugger.newConnectionReader(reader);
1043                writer = debugger.newConnectionWriter(writer);
1044            }
1045        }
1046    }
1047
1048    /**
1049     * Set the servers Entity Caps node
1050     * 
1051     * XMPPConnection holds this information in order to avoid a dependency to
1052     * smackx where EntityCapsManager lives from smack.
1053     * 
1054     * @param node
1055     */
1056    protected void setServiceCapsNode(String node) {
1057        serviceCapsNode = node;
1058    }
1059
1060    /**
1061     * Retrieve the servers Entity Caps node
1062     * 
1063     * XMPPConnection holds this information in order to avoid a dependency to
1064     * smackx where EntityCapsManager lives from smack.
1065     * 
1066     * @return the servers entity caps node
1067     */
1068    public String getServiceCapsNode() {
1069        return serviceCapsNode;
1070    }
1071
1072    /**
1073     * Returns true if the server supports roster versioning as defined in XEP-0237.
1074     *
1075     * @return true if the server supports roster versioning
1076     */
1077    public boolean isRosterVersioningSupported() {
1078        return rosterVersioningSupported;
1079    }
1080
1081    /**
1082     * Indicates that the server supports roster versioning as defined in XEP-0237.
1083     */
1084    protected void setRosterVersioningSupported() {
1085        rosterVersioningSupported = true;
1086    }
1087
1088    /**
1089     * Returns the current value of the reply timeout in milliseconds for request for this
1090     * XMPPConnection instance.
1091     *
1092     * @return the packet reply timeout in milliseconds
1093     */
1094    public long getPacketReplyTimeout() {
1095        return packetReplyTimeout;
1096    }
1097
1098    /**
1099     * Set the packet reply timeout in milliseconds. In most cases, Smack will throw a
1100     * {@link NoResponseException} if no reply to a request was received within the timeout period.
1101     *
1102     * @param timeout the packet reply timeout in milliseconds
1103     */
1104    public void setPacketReplyTimeout(long timeout) {
1105        packetReplyTimeout = timeout;
1106    }
1107
1108    /**
1109     * Processes a packet after it's been fully parsed by looping through the installed
1110     * packet collectors and listeners and letting them examine the packet to see if
1111     * they are a match with the filter.
1112     *
1113     * @param packet the packet to process.
1114     */
1115    protected void processPacket(Packet packet) {
1116        if (packet == null) {
1117            return;
1118        }
1119
1120        // Loop through all collectors and notify the appropriate ones.
1121        for (PacketCollector collector: getPacketCollectors()) {
1122            collector.processPacket(packet);
1123        }
1124
1125        // Deliver the incoming packet to listeners.
1126        executorService.submit(new ListenerNotification(packet));
1127    }
1128
1129    /**
1130     * A runnable to notify all listeners of a packet.
1131     */
1132    private class ListenerNotification implements Runnable {
1133
1134        private Packet packet;
1135
1136        public ListenerNotification(Packet packet) {
1137            this.packet = packet;
1138        }
1139
1140        public void run() {
1141            for (ListenerWrapper listenerWrapper : recvListeners.values()) {
1142                try {
1143                    listenerWrapper.notifyListener(packet);
1144                } catch(NotConnectedException e) {
1145                    LOGGER.log(Level.WARNING, "Got not connected exception, aborting", e);
1146                    break;
1147                } catch (Exception e) {
1148                    LOGGER.log(Level.SEVERE, "Exception in packet listener", e);
1149                }
1150            }
1151        }
1152    }
1153
1154    /**
1155     * Sets whether the connection has already logged in the server. This method assures that the
1156     * {@link #wasAuthenticated} flag is never reset once it has ever been set.
1157     * 
1158     * @param authenticated true if the connection has already been authenticated.
1159     */
1160    protected void setWasAuthenticated(boolean authenticated) {
1161        // Never reset the flag if the connection has ever been authenticated
1162        if (!wasAuthenticated) {
1163            wasAuthenticated = authenticated;
1164        }
1165    }
1166
1167    protected void callConnectionConnectedListener() {
1168        for (ConnectionListener listener : getConnectionListeners()) {
1169            listener.connected(this);
1170        }
1171    }
1172
1173    protected void callConnectionAuthenticatedListener() {
1174        for (ConnectionListener listener : getConnectionListeners()) {
1175            listener.authenticated(this);
1176        }
1177    }
1178
1179    void callConnectionClosedListener() {
1180        for (ConnectionListener listener : getConnectionListeners()) {
1181            try {
1182                listener.connectionClosed();
1183            }
1184            catch (Exception e) {
1185                // Catch and print any exception so we can recover
1186                // from a faulty listener and finish the shutdown process
1187                LOGGER.log(Level.SEVERE, "Error in listener while closing connection", e);
1188            }
1189        }
1190    }
1191
1192    protected void callConnectionClosedOnErrorListener(Exception e) {
1193        LOGGER.log(Level.WARNING, "Connection closed with error", e);
1194        for (ConnectionListener listener : getConnectionListeners()) {
1195            try {
1196                listener.connectionClosedOnError(e);
1197            }
1198            catch (Exception e2) {
1199                // Catch and print any exception so we can recover
1200                // from a faulty listener
1201                LOGGER.log(Level.SEVERE, "Error in listener while closing connection", e2);
1202            }
1203        }
1204    }
1205
1206    /**
1207     * A wrapper class to associate a packet filter with a listener.
1208     */
1209    protected static class ListenerWrapper {
1210
1211        private PacketListener packetListener;
1212        private PacketFilter packetFilter;
1213
1214        /**
1215         * Create a class which associates a packet filter with a listener.
1216         * 
1217         * @param packetListener the packet listener.
1218         * @param packetFilter the associated filter or null if it listen for all packets.
1219         */
1220        public ListenerWrapper(PacketListener packetListener, PacketFilter packetFilter) {
1221            this.packetListener = packetListener;
1222            this.packetFilter = packetFilter;
1223        }
1224
1225        /**
1226         * Notify and process the packet listener if the filter matches the packet.
1227         * 
1228         * @param packet the packet which was sent or received.
1229         * @throws NotConnectedException 
1230         */
1231        public void notifyListener(Packet packet) throws NotConnectedException {
1232            if (packetFilter == null || packetFilter.accept(packet)) {
1233                packetListener.processPacket(packet);
1234            }
1235        }
1236    }
1237
1238    /**
1239     * A wrapper class to associate a packet filter with an interceptor.
1240     */
1241    protected static class InterceptorWrapper {
1242
1243        private PacketInterceptor packetInterceptor;
1244        private PacketFilter packetFilter;
1245
1246        /**
1247         * Create a class which associates a packet filter with an interceptor.
1248         * 
1249         * @param packetInterceptor the interceptor.
1250         * @param packetFilter the associated filter or null if it intercepts all packets.
1251         */
1252        public InterceptorWrapper(PacketInterceptor packetInterceptor, PacketFilter packetFilter) {
1253            this.packetInterceptor = packetInterceptor;
1254            this.packetFilter = packetFilter;
1255        }
1256
1257        public boolean equals(Object object) {
1258            if (object == null) {
1259                return false;
1260            }
1261            if (object instanceof InterceptorWrapper) {
1262                return ((InterceptorWrapper) object).packetInterceptor
1263                        .equals(this.packetInterceptor);
1264            }
1265            else if (object instanceof PacketInterceptor) {
1266                return object.equals(this.packetInterceptor);
1267            }
1268            return false;
1269        }
1270
1271        /**
1272         * Notify and process the packet interceptor if the filter matches the packet.
1273         * 
1274         * @param packet the packet which will be sent.
1275         */
1276        public void notifyListener(Packet packet) {
1277            if (packetFilter == null || packetFilter.accept(packet)) {
1278                packetInterceptor.interceptPacket(packet);
1279            }
1280        }
1281    }
1282
1283    protected ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
1284        return executorService.schedule(command, delay, unit);
1285    }
1286
1287    /**
1288     * Get the connection counter of this XMPPConnection instance. Those can be used as ID to
1289     * identify the connection, but beware that the ID may not be unique if you create more then
1290     * <tt>2*Integer.MAX_VALUE</tt> instances as the counter could wrap.
1291     *
1292     * @return the connection counter of this XMPPConnection
1293     */
1294    public int getConnectionCounter() {
1295        return connectionCounterValue;
1296    }
1297
1298    public static enum FromMode {
1299        /**
1300         * Leave the 'from' attribute unchanged. This is the behavior of Smack < 4.0
1301         */
1302        UNCHANGED,
1303        /**
1304         * Omit the 'from' attribute. According to RFC 6120 8.1.2.1 1. XMPP servers "MUST (...)
1305         * override the 'from' attribute specified by the client". It is therefore safe to specify
1306         * FromMode.OMITTED here.
1307         */
1308        OMITTED,
1309        /**
1310         * Set the from to the clients full JID. This is usually not required.
1311         */
1312        USER
1313    }
1314
1315    /**
1316     * Set the FromMode for this connection instance. Defines how the 'from' attribute of outgoing
1317     * stanzas should be populated by Smack.
1318     * 
1319     * @param fromMode
1320     */
1321    public void setFromMode(FromMode fromMode) {
1322        this.fromMode = fromMode;
1323    }
1324
1325    /**
1326     * Get the currently active FromMode.
1327     *
1328     * @return the currently active {@link FromMode}
1329     */
1330    public FromMode getFromMode() {
1331        return this.fromMode;
1332    }
1333
1334    @Override
1335    protected void finalize() throws Throwable {
1336        try {
1337            // It's usually not a good idea to rely on finalize. But this is the easiest way to
1338            // avoid the "Smack Listener Processor" leaking. The thread(s) of the executor have a
1339            // reference to their ExecutorService which prevents the ExecutorService from being
1340            // gc'ed. It is possible that the XMPPConnection instance is gc'ed while the
1341            // listenerExecutor ExecutorService call not be gc'ed until it got shut down.
1342            executorService.shutdownNow();
1343        }
1344        finally {
1345            super.finalize();
1346        }
1347    }
1348}