1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/*
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Copyright 2009 Mike Cumings
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Licensed under the Apache License, Version 2.0 (the "License");
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License.
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *   http://www.apache.org/licenses/LICENSE-2.0
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS,
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License.
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage com.kenai.jbosh;
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport com.kenai.jbosh.ComposableBody.Builder;
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.ArrayList;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Iterator;
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.LinkedList;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.List;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Queue;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Set;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.SortedSet;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.TreeSet;
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.CopyOnWriteArraySet;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.Executors;
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.RejectedExecutionException;
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ScheduledExecutorService;
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ScheduledFuture;
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.TimeUnit;
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.atomic.AtomicReference;
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.locks.Condition;
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.locks.ReentrantLock;
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.logging.Level;
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.logging.Logger;
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * BOSH Client session instance.  Each communication session with a remote
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * connection manager is represented and handled by an instance of this
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * class.  This is the main entry point for client-side communications.
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * To create a new session, a client configuration must first be created
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * and then used to create a client instance:
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <pre>
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * BOSHClientConfig cfg = BOSHClientConfig.Builder.create(
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *         "http://server:1234/httpbind", "jabber.org")
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *     .setFrom("user@jabber.org")
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *     .build();
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * BOSHClient client = BOSHClient.create(cfg);
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * </pre>
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Additional client configuration options are available.  See the
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@code BOSHClientConfig.Builder} class for more information.
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/>
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Once a {@code BOSHClient} instance has been created, communication with
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the remote connection manager can begin.  No attempt will be made to
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * establish a connection to the connection manager until the first call
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * is made to the {@code send(ComposableBody)} method.  Note that it is
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * possible to send an empty body to cause an immediate connection attempt
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * to the connection manager.  Sending an empty message would look like
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the following:
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <pre>
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * client.send(ComposableBody.builder().build());
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * </pre>
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * For more information on creating body messages with content, see the
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@code ComposableBody.Builder} class documentation.
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/>
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Once a session has been successfully started, the client instance can be
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * used to send arbitrary payload data.  All aspects of the BOSH
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * protocol involving setting and processing attributes in the BOSH
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * namespace will be handled by the client code transparently and behind the
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * scenes.  The user of the client instance can therefore concentrate
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * entirely on the content of the message payload, leaving the semantics of
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the BOSH protocol to the client implementation.
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/>
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * To be notified of incoming messages from the remote connection manager,
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * a {@code BOSHClientResponseListener} should be added to the client instance.
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All incoming messages will be published to all response listeners as they
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * arrive and are processed.  As with the transmission of payload data via
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * the {@code send(ComposableBody)} method, there is no need to worry about
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * handling of the BOSH attributes, since this is handled behind the scenes.
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/>
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If the connection to the remote connection manager is terminated (either
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * explicitly or due to a terminal condition of some sort), all connection
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * listeners will be notified.  After the connection has been closed, the
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * client instance is considered dead and a new one must be created in order
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * to resume communications with the remote server.
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p/>
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Instances of this class are thread-safe.
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see BOSHClientConfig.Builder
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see BOSHClientResponseListener
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see BOSHClientConnListener
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @see ComposableBody.Builder
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic final class BOSHClient {
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Logger.
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final Logger LOG = Logger.getLogger(
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            BOSHClient.class.getName());
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Value of the 'type' attribute used for session termination.
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String TERMINATE = "terminate";
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Value of the 'type' attribute used for recoverable errors.
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String ERROR = "error";
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Message to use for interrupted exceptions.
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String INTERRUPTED = "Interrupted";
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Message used for unhandled exceptions.
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String UNHANDLED = "Unhandled Exception";
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Message used whena null listener is detected.
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String NULL_LISTENER = "Listener may not b enull";
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Default empty request delay.
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final int DEFAULT_EMPTY_REQUEST_DELAY = 100;
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Amount of time to wait before sending an empty request, in
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * milliseconds.
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final int EMPTY_REQUEST_DELAY = Integer.getInteger(
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            BOSHClient.class.getName() + ".emptyRequestDelay",
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            DEFAULT_EMPTY_REQUEST_DELAY);
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Default value for the pause margin.
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final int DEFAULT_PAUSE_MARGIN = 500;
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The amount of time in milliseconds which will be reserved as a
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * safety margin when scheduling empty requests against a maxpause
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * value.   This should give us enough time to build the message
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * and transport it to the remote host.
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final int PAUSE_MARGIN = Integer.getInteger(
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            BOSHClient.class.getName() + ".pauseMargin",
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            DEFAULT_PAUSE_MARGIN);
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Flag indicating whether or not we want to perform assertions.
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final boolean ASSERTIONS;
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Connection listeners.
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Set<BOSHClientConnListener> connListeners =
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new CopyOnWriteArraySet<BOSHClientConnListener>();
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Request listeners.
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Set<BOSHClientRequestListener> requestListeners =
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new CopyOnWriteArraySet<BOSHClientRequestListener>();
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Response listeners.
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Set<BOSHClientResponseListener> responseListeners =
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new CopyOnWriteArraySet<BOSHClientResponseListener>();
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Lock instance.
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final ReentrantLock lock = new ReentrantLock();
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Condition indicating that there are messages to be exchanged.
188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Condition notEmpty = lock.newCondition();
190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Condition indicating that there are available slots for sending
193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * messages.
194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Condition notFull = lock.newCondition();
196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Condition indicating that there are no outstanding connections.
199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Condition drained = lock.newCondition();
201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Session configuration.
204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final BOSHClientConfig cfg;
206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Processor thread runnable instance.
209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Runnable procRunnable = new Runnable() {
211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * Process incoming messages.
213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public void run() {
215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            processMessages();
216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    };
218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Processor thread runnable instance.
221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Runnable emptyRequestRunnable = new Runnable() {
223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * Process incoming messages.
225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public void run() {
227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            sendEmptyRequest();
228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    };
230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * HTTPSender instance.
233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final HTTPSender httpSender =
235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new ApacheHTTPSender();
236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Storage for test hook implementation.
239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final AtomicReference<ExchangeInterceptor> exchInterceptor =
241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new AtomicReference<ExchangeInterceptor>();
242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Request ID sequence to use for the session.
245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final RequestIDSequence requestIDSeq = new RequestIDSequence();
247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * ScheduledExcecutor to use for deferred tasks.
250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final ScheduledExecutorService schedExec =
252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Executors.newSingleThreadScheduledExecutor();
253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /************************************************************
255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The following vars must be accessed via the lock instance.
256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Thread which is used to process responses from the connection
260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * manager.  Becomes null when session is terminated.
261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Thread procThread;
263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Future for sending a deferred empty request, if needed.
266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private ScheduledFuture emptyRequestFuture;
268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Connection Manager session parameters.  Only available when in a
271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * connected state.
272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private CMSessionParams cmParams;
274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * List of active/outstanding requests.
277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Queue<HTTPExchange> exchanges = new LinkedList<HTTPExchange>();
279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Set of RIDs which have been received, for the purpose of sending
282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * response acknowledgements.
283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private SortedSet<Long> pendingResponseAcks = new TreeSet<Long>();
285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The highest RID that we've already received a response for.  This value
288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * is used to implement response acks.
289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Long responseAck = Long.valueOf(-1L);
291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * List of requests which have been made but not yet acknowledged.  This
294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * list remains unpopulated if the CM is not acking requests.
295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private List<ComposableBody> pendingRequestAcks =
297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            new ArrayList<ComposableBody>();
298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Classes:
301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Class used in testing to dynamically manipulate received exchanges
304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * at test runtime.
305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    abstract static class ExchangeInterceptor {
307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * Limit construction.
309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ExchangeInterceptor() {
311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Empty;
312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * Hook to manipulate an HTTPExchange as is is about to be processed.
316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         *
317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * @param exch original exchange that would be processed
318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * @return replacement exchange instance, or {@code null} to skip
319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         *  processing of this exchange
320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        abstract HTTPExchange interceptExchange(final HTTPExchange exch);
322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Constructors:
326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determine whether or not we should perform assertions.  Assertions
329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * can be specified via system property explicitly, or defaulted to
330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the JVM assertions status.
331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    static {
333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        final String prop =
334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                BOSHClient.class.getSimpleName() + ".assertionsEnabled";
335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        boolean enabled = false;
336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (System.getProperty(prop) == null) {
337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            assert enabled = true;
338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            enabled = Boolean.getBoolean(prop);
340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ASSERTIONS = enabled;
342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Prevent direct construction.
346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private BOSHClient(final BOSHClientConfig sessCfg) {
348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        cfg = sessCfg;
349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        init();
350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Public methods:
354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Create a new BOSH client session using the client configuration
357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * information provided.
358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param clientCfg session configuration
360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return BOSH session instance
361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public static BOSHClient create(final BOSHClientConfig clientCfg) {
363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (clientCfg == null) {
364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(
365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Client configuration may not be null"));
366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return new BOSHClient(clientCfg);
368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Get the client configuration that was used to create this client
372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * instance.
373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return client configuration
375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public BOSHClientConfig getBOSHClientConfig() {
377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return cfg;
378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds a connection listener to the session.
382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener connection listener to add, if not already added
384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addBOSHClientConnListener(
386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientConnListener listener) {
387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        connListeners.add(listener);
391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes a connection listener from the session.
395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener connection listener to remove, if previously added
397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeBOSHClientConnListener(
399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientConnListener listener) {
400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        connListeners.remove(listener);
404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds a request message listener to the session.
408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener request listener to add, if not already added
410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addBOSHClientRequestListener(
412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientRequestListener listener) {
413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        requestListeners.add(listener);
417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes a request message listener from the session, if previously
421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * added.
422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener instance to remove
424d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
425d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeBOSHClientRequestListener(
426d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientRequestListener listener) {
427d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
428d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
429d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
430d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        requestListeners.remove(listener);
431d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
432d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
433d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
434d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds a response message listener to the session.
435d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
436d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener response listener to add, if not already added
437d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
438d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addBOSHClientResponseListener(
439d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientResponseListener listener) {
440d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
441d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
442d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
443d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        responseListeners.add(listener);
444d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
445d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
446d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
447d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes a response message listener from the session, if previously
448d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * added.
449d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
450d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener instance to remove
451d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
452d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeBOSHClientResponseListener(
453d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final BOSHClientResponseListener listener) {
454d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (listener == null) {
455d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(NULL_LISTENER));
456d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
457d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        responseListeners.remove(listener);
458d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
459d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
460d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
461d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Send the provided message data to the remote connection manager.  The
462d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * provided message body does not need to have any BOSH-specific attribute
463d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * information set.  It only needs to contain the actual message payload
464d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * that should be delivered to the remote server.
465d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p/>
466d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The first call to this method will result in a connection attempt
467d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * to the remote connection manager.  Subsequent calls to this method
468d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * will block until the underlying session state allows for the message
469d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * to be transmitted.  In certain scenarios - such as when the maximum
470d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * number of outbound connections has been reached - calls to this method
471d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * will block for short periods of time.
472d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
473d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param body message data to send to remote server
474d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws BOSHException on message transmission failure
475d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
476d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void send(final ComposableBody body) throws BOSHException {
477d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
478d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (body == null) {
479d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(
480d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Message body may not be null"));
481d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
482d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
483d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        HTTPExchange exch;
484d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        CMSessionParams params;
485d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
486d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
487d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            blockUntilSendable(body);
488d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (!isWorking() && !isTermination(body)) {
489d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw(new BOSHException(
490d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        "Cannot send message when session is closed"));
491d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
492d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
493d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            long rid = requestIDSeq.getNextRID();
494d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            ComposableBody request = body;
495d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            params = cmParams;
496d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (params == null && exchanges.isEmpty()) {
497d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // This is the first message being sent
498d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                request = applySessionCreationRequest(rid, body);
499d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } else {
500d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                request = applySessionData(rid, body);
501d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (cmParams.isAckingRequests()) {
502d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    pendingRequestAcks.add(request);
503d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
504d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
505d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            exch = new HTTPExchange(request);
506d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            exchanges.add(exch);
507d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            notEmpty.signalAll();
508d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            clearEmptyRequest();
509d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
510d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
511d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
512d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AbstractBody finalReq = exch.getRequest();
513d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        HTTPResponse resp = httpSender.send(params, finalReq);
514d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        exch.setHTTPResponse(resp);
515d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        fireRequestSent(finalReq);
516d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
517d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
518d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
519d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Attempt to pause the current session.  When supported by the remote
520d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * connection manager, pausing the session will result in the connection
521d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * manager closing out all outstanding requests (including the pause
522d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * request) and increases the inactivity timeout of the session.  The
523d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * exact value of the temporary timeout is dependent upon the connection
524d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * manager.  This method should be used if a client encounters an
525d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * exceptional temporary situation during which it will be unable to send
526d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * requests to the connection manager for a period of time greater than
527d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the maximum inactivity period.
528d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
529d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The session will revert back to it's normal, unpaused state when the
530d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * client sends it's next message.
531d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
532d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if the connection manager supports session pausing,
533d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  {@code false} if the connection manager does not support session
534d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  pausing or if the session has not yet been established
535d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
536d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public boolean pause() {
537d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
538d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
539d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AttrMaxPause maxPause = null;
540d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
541d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (cmParams == null) {
542d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return false;
543d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
544d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
545d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            maxPause = cmParams.getMaxPause();
546d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (maxPause == null) {
547d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return false;
548d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
549d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
550d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
551d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
552d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
553d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            send(ComposableBody.builder()
554d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    .setAttribute(Attributes.PAUSE, maxPause.toString())
555d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    .build());
556d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (BOSHException boshx) {
557d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, "Could not send pause", boshx);
558d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
559d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return true;
560d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
561d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
562d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
563d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * End the BOSH session by disconnecting from the remote BOSH connection
564d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * manager.
565d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
566d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws BOSHException when termination message cannot be sent
567d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
568d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void disconnect() throws BOSHException {
569d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        disconnect(ComposableBody.builder().build());
570d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
571d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
572d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
573d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * End the BOSH session by disconnecting from the remote BOSH connection
574d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * manager, sending the provided content in the final connection
575d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * termination message.
576d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
577d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param msg final message to send
578d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws BOSHException when termination message cannot be sent
579d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
580d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void disconnect(final ComposableBody msg) throws BOSHException {
581d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (msg == null) {
582d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(
583d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Message body may not be null"));
584d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
585d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
586d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Builder builder = msg.rebuild();
587d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.TYPE, TERMINATE);
588d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        send(builder.build());
589d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
590d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
591d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
592d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Forcibly close this client session instance.  The preferred mechanism
593d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * to close the connection is to send a disconnect message and wait for
594d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * organic termination.  Calling this method simply shuts down the local
595d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * session without sending a termination message, releasing all resources
596d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * associated with the session.
597d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
598d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void close() {
599d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        dispose(new BOSHException("Session explicitly closed by caller"));
600d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
601d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
602d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
603d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Package-private methods:
604d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
605d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
606d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Get the current CM session params.
607d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
608d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return current session params, or {@code null}
609d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
610d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    CMSessionParams getCMSessionParams() {
611d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
612d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
613d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return cmParams;
614d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
615d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
616d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
617d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
618d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
619d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
620d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Wait until no more messages are waiting to be processed.
621d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
622d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    void drain() {
623d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
624d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
625d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.finest("Waiting while draining...");
626d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            while (isWorking()
627d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    && (emptyRequestFuture == null
628d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    || emptyRequestFuture.isDone())) {
629d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
630d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    drained.await();
631d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                } catch (InterruptedException intx) {
632d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    LOG.log(Level.FINEST, INTERRUPTED, intx);
633d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
634d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
635d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.finest("Drained");
636d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
637d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
638d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
639d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
640d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
641d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
642d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Test method used to forcibly discard next exchange.
643d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
644d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param interceptor exchange interceptor
645d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
646d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    void setExchangeInterceptor(final ExchangeInterceptor interceptor) {
647d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        exchInterceptor.set(interceptor);
648d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
649d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
650d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
651d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    ///////////////////////////////////////////////////////////////////////////
652d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    // Private methods:
653d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
654d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
655d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Initialize the session.  This initializes the underlying HTTP
656d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * transport implementation and starts the receive thread.
657d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
658d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void init() {
659d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
660d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
661d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
662d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
663d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            httpSender.init(cfg);
664d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            procThread = new Thread(procRunnable);
665d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            procThread.setDaemon(true);
666d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            procThread.setName(BOSHClient.class.getSimpleName()
667d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + "[" + System.identityHashCode(this)
668d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + "]: Receive thread");
669d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            procThread.start();
670d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
671d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
672d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
673d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
674d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
675d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
676d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Destroy this session.
677d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
678d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param cause the reason for the session termination, or {@code null}
679d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  for normal termination
680d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
681d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void dispose(final Throwable cause) {
682d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
683d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
684d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
685d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
686d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (procThread == null) {
687d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // Already disposed
688d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return;
689d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
690d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            procThread = null;
691d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
692d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
693d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
694d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
695d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (cause == null) {
696d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            fireConnectionClosed();
697d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
698d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            fireConnectionClosedOnError(cause);
699d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
700d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
701d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
702d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
703d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            clearEmptyRequest();
704d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            exchanges = null;
705d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            cmParams = null;
706d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            pendingResponseAcks = null;
707d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            pendingRequestAcks = null;
708d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            notEmpty.signalAll();
709d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            notFull.signalAll();
710d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            drained.signalAll();
711d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
712d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
713d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
714d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
715d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        httpSender.destroy();
716d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        schedExec.shutdownNow();
717d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
718d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
719d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
720d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determines if the message body specified indicates a request to
721d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * pause the session.
722d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
723d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param msg message to evaluate
724d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if the message is a pause request, {@code false}
725d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  otherwise
726d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
727d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static boolean isPause(final AbstractBody msg) {
728d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return msg.getAttribute(Attributes.PAUSE) != null;
729d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
730d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
731d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
732d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determines if the message body specified indicates a termination of
733d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the session.
734d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
735d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param msg message to evaluate
736d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if the message is a session termination,
737d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  {@code false} otherwise
738d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
739d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static boolean isTermination(final AbstractBody msg) {
740d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return TERMINATE.equals(msg.getAttribute(Attributes.TYPE));
741d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
742d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
743d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
744d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Evaluates the HTTP response code and response message and returns the
745d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * terminal binding condition that it describes, if any.
746d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
747d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param respCode HTTP response code
748d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param respBody response body
749d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return terminal binding condition, or {@code null} if not a terminal
750d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  binding condition message
751d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
752d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private TerminalBindingCondition getTerminalBindingCondition(
753d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final int respCode,
754d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody respBody) {
755d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
756d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
757d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (isTermination(respBody)) {
758d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            String str = respBody.getAttribute(Attributes.CONDITION);
759d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return TerminalBindingCondition.forString(str);
760d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
761d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Check for deprecated HTTP Error Conditions
762d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (cmParams != null && cmParams.getVersion() == null) {
763d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return TerminalBindingCondition.forHTTPResponseCode(respCode);
764d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
765d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return null;
766d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
767d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
768d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
769d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determines if the message specified is immediately sendable or if it
770d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * needs to block until the session state changes.
771d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
772d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param msg message to evaluate
773d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if the message can be immediately sent,
774d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  {@code false} otherwise
775d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
776d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private boolean isImmediatelySendable(final AbstractBody msg) {
777d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
778d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
779d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (cmParams == null) {
780d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // block if we're waiting for a response to our first request
781d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return exchanges.isEmpty();
782d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
783d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
784d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AttrRequests requests = cmParams.getRequests();
785d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (requests == null) {
786d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return true;
787d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
788d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        int maxRequests = requests.intValue();
789d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (exchanges.size() < maxRequests) {
790d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return true;
791d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
792d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (exchanges.size() == maxRequests
793d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                && (isTermination(msg) || isPause(msg))) {
794d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // One additional terminate or pause message is allowed
795d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return true;
796d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
797d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return false;
798d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
799d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
800d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
801d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determines whether or not the session is still active.
802d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
803d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if it is, {@code false} otherwise
804d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
805d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private boolean isWorking() {
806d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
807d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
808d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return procThread != null;
809d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
810d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
811d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
812d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Blocks until either the message provided becomes immediately
813d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * sendable or until the session is terminated.
814d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
815d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param msg message to evaluate
816d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
817d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void blockUntilSendable(final AbstractBody msg) {
818d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
819d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
820d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        while (isWorking() && !isImmediatelySendable(msg)) {
821d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
822d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                notFull.await();
823d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (InterruptedException intx) {
824d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.FINEST, INTERRUPTED, intx);
825d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
826d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
827d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
828d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
829d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
830d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Modifies the specified body message such that it becomes a new
831d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * BOSH session creation request.
832d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
833d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param rid request ID to use
834d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param orig original body to modify
835d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return modified message which acts as a session creation request
836d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
837d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private ComposableBody applySessionCreationRequest(
838d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final long rid, final ComposableBody orig) throws BOSHException {
839d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
840d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
841d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Builder builder = orig.rebuild();
842d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.TO, cfg.getTo());
843d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.XML_LANG, cfg.getLang());
844d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.VER,
845d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                AttrVersion.getSupportedVersion().toString());
846d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.WAIT, "60");
847d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.HOLD, "1");
848d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.RID, Long.toString(rid));
849d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        applyRoute(builder);
850d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        applyFrom(builder);
851d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.ACK, "1");
852d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
853d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Make sure the following are NOT present (i.e., during retries)
854d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.SID, null);
855d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return builder.build();
856d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
857d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
858d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
859d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Applies routing information to the request message who's builder has
860d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * been provided.
861d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
862d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param builder builder instance to add routing information to
863d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
864d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void applyRoute(final Builder builder) {
865d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
866d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
867d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String route = cfg.getRoute();
868d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (route != null) {
869d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            builder.setAttribute(Attributes.ROUTE, route);
870d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
871d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
872d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
873d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
874d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Applies the local station ID information to the request message who's
875d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * builder has been provided.
876d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
877d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param builder builder instance to add station ID information to
878d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
879d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void applyFrom(final Builder builder) {
880d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
881d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
882d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String from = cfg.getFrom();
883d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (from != null) {
884d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            builder.setAttribute(Attributes.FROM, from);
885d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
886d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
887d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
888d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
889d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Applies existing session data to the outbound request, returning the
890d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * modified request.
891d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
892d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method assumes the lock is currently held.
893d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
894d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param rid request ID to use
895d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param orig original/raw request
896d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return modified request with session information applied
897d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
898d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private ComposableBody applySessionData(
899d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final long rid,
900d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final ComposableBody orig) throws BOSHException {
901d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
902d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
903d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Builder builder = orig.rebuild();
904d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.SID,
905d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                cmParams.getSessionID().toString());
906d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.RID, Long.toString(rid));
907d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        applyResponseAcknowledgement(builder, rid);
908d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return builder.build();
909d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
910d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
911d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
912d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Sets the 'ack' attribute of the request to the value of the highest
913d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * 'rid' of a request for which it has already received a response in the
914d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * case where it has also received all responses associated with lower
915d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * 'rid' values.  The only exception is that, after its session creation
916d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * request, the client SHOULD NOT include an 'ack' attribute in any request
917d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * if it has received responses to all its previous requests.
918d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
919d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param builder message builder
920d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param rid current request RID
921d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
922d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void applyResponseAcknowledgement(
923d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final Builder builder,
924d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final long rid) {
925d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
926d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
927d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (responseAck.equals(Long.valueOf(-1L))) {
928d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // We have not received any responses yet
929d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
930d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
931d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
932d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Long prevRID = Long.valueOf(rid - 1L);
933d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (responseAck.equals(prevRID)) {
934d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Implicit ack
935d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
936d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
937d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
938d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        builder.setAttribute(Attributes.ACK, responseAck.toString());
939d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
940d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
941d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
942d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * While we are "connected", process received responses.
943d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
944d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method is run in the processing thread.
945d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
946d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void processMessages() {
947d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        LOG.log(Level.FINEST, "Processing thread starting");
948d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
949d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            HTTPExchange exch;
950d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            do {
951d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                exch = nextExchange();
952d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (exch == null) {
953d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    break;
954d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
955d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
956d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // Test hook to manipulate what the client sees:
957d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                ExchangeInterceptor interceptor = exchInterceptor.get();
958d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (interceptor != null) {
959d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    HTTPExchange newExch = interceptor.interceptExchange(exch);
960d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    if (newExch == null) {
961d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        LOG.log(Level.FINE, "Discarding exchange on request "
962d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                + "of test hook: RID="
963d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                + exch.getRequest().getAttribute(
964d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                    Attributes.RID));
965d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        lock.lock();
966d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        try {
967d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            exchanges.remove(exch);
968d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        } finally {
969d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            lock.unlock();
970d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        }
971d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        continue;
972d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
973d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    exch = newExch;
974d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
975d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
976d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                processExchange(exch);
977d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } while (true);
978d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
979d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, "Processing thread exiting");
980d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
981d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
982d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
983d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
984d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
985d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Get the next message exchange to process, blocking until one becomes
986d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * available if nothing is already waiting for processing.
987d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
988d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return next available exchange to process, or {@code null} if no
989d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  exchanges are immediately available
990d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
991d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private HTTPExchange nextExchange() {
992d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
993d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
994d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        final Thread thread = Thread.currentThread();
995d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        HTTPExchange exch = null;
996d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
997d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
998d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            do {
999d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (!thread.equals(procThread)) {
1000d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    break;
1001d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1002d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                exch = exchanges.peek();
1003d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (exch == null) {
1004d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    try {
1005d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        notEmpty.await();
1006d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    } catch (InterruptedException intx) {
1007d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        LOG.log(Level.FINEST, INTERRUPTED, intx);
1008d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
1009d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1010d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } while (exch == null);
1011d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
1012d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
1013d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1014d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return exch;
1015d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1016d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1017d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1018d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Process the next, provided exchange.  This is the main processing
1019d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * method of the receive thread.
1020d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1021d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param exch message exchange to process
1022d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1023d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void processExchange(final HTTPExchange exch) {
1024d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1025d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1026d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        HTTPResponse resp;
1027d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AbstractBody body;
1028d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        int respCode;
1029d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
1030d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            resp = exch.getHTTPResponse();
1031d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            body = resp.getBody();
1032d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            respCode = resp.getHTTPStatus();
1033d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (BOSHException boshx) {
1034d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, "Could not obtain response", boshx);
1035d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dispose(boshx);
1036d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1037d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (InterruptedException intx) {
1038d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, INTERRUPTED, intx);
1039d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dispose(intx);
1040d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1041d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1042d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        fireResponseReceived(body);
1043d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1044d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Process the message with the current session state
1045d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AbstractBody req = exch.getRequest();
1046d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        CMSessionParams params;
1047d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<HTTPExchange> toResend = null;
1048d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        lock.lock();
1049d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
1050d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Check for session creation response info, if needed
1051d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (cmParams == null) {
1052d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                cmParams = CMSessionParams.fromSessionInit(req, body);
1053d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1054d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // The following call handles the lock. It's not an escape.
1055d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                fireConnectionEstablished();
1056d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1057d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            params = cmParams;
1058d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1059d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            checkForTerminalBindingConditions(body, respCode);
1060d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (isTermination(body)) {
1061d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // Explicit termination
1062d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                lock.unlock();
1063d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                dispose(null);
1064d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return;
1065d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1066d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1067d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (isRecoverableBindingCondition(body)) {
1068d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // Retransmit outstanding requests
1069d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (toResend == null) {
1070d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    toResend = new ArrayList<HTTPExchange>(exchanges.size());
1071d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1072d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                for (HTTPExchange exchange : exchanges) {
1073d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    HTTPExchange resendExch =
1074d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            new HTTPExchange(exchange.getRequest());
1075d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    toResend.add(resendExch);
1076d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1077d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                for (HTTPExchange exchange : toResend) {
1078d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    exchanges.add(exchange);
1079d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1080d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } else {
1081d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // Process message as normal
1082d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                processRequestAcknowledgements(req, body);
1083d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                processResponseAcknowledgementData(req);
1084d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                HTTPExchange resendExch =
1085d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        processResponseAcknowledgementReport(body);
1086d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (resendExch != null && toResend == null) {
1087d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    toResend = new ArrayList<HTTPExchange>(1);
1088d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    toResend.add(resendExch);
1089d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    exchanges.add(resendExch);
1090d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1091d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1092d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (BOSHException boshx) {
1093d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, "Could not process response", boshx);
1094d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
1095d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dispose(boshx);
1096d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1097d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
1098d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (lock.isHeldByCurrentThread()) {
1099d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
1100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    exchanges.remove(exch);
1101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    if (exchanges.isEmpty()) {
1102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        scheduleEmptyRequest(processPauseRequest(req));
1103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
1104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    notFull.signalAll();
1105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                } finally {
1106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    lock.unlock();
1107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (toResend != null) {
1112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (HTTPExchange resend : toResend) {
1113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                HTTPResponse response =
1114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        httpSender.send(params, resend.getRequest());
1115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                resend.setHTTPResponse(response);
1116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                fireRequestSent(resend.getRequest());
1117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Clears any scheduled empty requests.
1123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void clearEmptyRequest() {
1125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (emptyRequestFuture != null) {
1128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            emptyRequestFuture.cancel(false);
1129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            emptyRequestFuture = null;
1130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Calculates the default empty request delay/interval to use for the
1135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * active session.
1136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return delay in milliseconds
1138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private long getDefaultEmptyRequestDelay() {
1140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Figure out how long we should wait before sending an empty request
1143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AttrPolling polling = cmParams.getPollingInterval();
1144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        long delay;
1145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (polling == null) {
1146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            delay = EMPTY_REQUEST_DELAY;
1147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
1148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            delay = polling.getInMilliseconds();
1149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return delay;
1151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Schedule an empty request to be sent if no other requests are
1155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * sent in a reasonable amount of time.
1156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void scheduleEmptyRequest(long delay) {
1158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (delay < 0L) {
1160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new IllegalArgumentException(
1161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Empty request delay must be >= 0 (was: " + delay + ")"));
1162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        clearEmptyRequest();
1165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!isWorking()) {
1166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Schedule the transmission
1170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (LOG.isLoggable(Level.FINER)) {
1171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.finer("Scheduling empty request in " + delay + "ms");
1172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
1174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            emptyRequestFuture = schedExec.schedule(emptyRequestRunnable,
1175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    delay, TimeUnit.MILLISECONDS);
1176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (RejectedExecutionException rex) {
1177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.log(Level.FINEST, "Could not schedule empty request", rex);
1178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        drained.signalAll();
1180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Sends an empty request to maintain session requirements.  If a request
1184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * is sent within a reasonable time window, the empty request transmission
1185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * will be cancelled.
1186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void sendEmptyRequest() {
1188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Send an empty request
1190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        LOG.finest("Sending empty request");
1191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
1192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            send(ComposableBody.builder().build());
1193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (BOSHException boshx) {
1194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dispose(boshx);
1195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Assert that the internal lock is held.
1200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void assertLocked() {
1202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (ASSERTIONS) {
1203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (!lock.isHeldByCurrentThread()) {
1204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw(new AssertionError("Lock is not held by current thread"));
1205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Assert that the internal lock is *not* held.
1212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void assertUnlocked() {
1214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (ASSERTIONS) {
1215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (lock.isHeldByCurrentThread()) {
1216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw(new AssertionError("Lock is held by current thread"));
1217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Checks to see if the response indicates a terminal binding condition
1224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * (as per XEP-0124 section 17).  If it does, an exception is thrown.
1225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param body response body to evaluate
1227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param code HTTP response code
1228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws BOSHException if a terminal binding condition is detected
1229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void checkForTerminalBindingConditions(
1231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody body,
1232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final int code)
1233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throws BOSHException {
1234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        TerminalBindingCondition cond =
1235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                getTerminalBindingCondition(code, body);
1236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (cond != null) {
1237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new BOSHException(
1238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    "Terminal binding condition encountered: "
1239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + cond.getCondition() + "  ("
1240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + cond.getMessage() + ")"));
1241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Determines whether or not the response indicates a recoverable
1246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * binding condition (as per XEP-0124 section 17).
1247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param resp response body
1249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return {@code true} if it does, {@code false} otherwise
1250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static boolean isRecoverableBindingCondition(
1252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody resp) {
1253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return ERROR.equals(resp.getAttribute(Attributes.TYPE));
1254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Process the request to determine if the empty request delay
1258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * can be determined by looking to see if the request is a pause
1259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * request.  If it can, the request's delay is returned, otherwise
1260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the default delay is returned.
1261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return delay in milliseconds that should elapse prior to an
1263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  empty message being sent
1264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private long processPauseRequest(
1266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody req) {
1267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (cmParams != null && cmParams.getMaxPause() != null) {
1270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
1271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                AttrPause pause = AttrPause.createFromString(
1272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        req.getAttribute(Attributes.PAUSE));
1273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (pause != null) {
1274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    long delay = pause.getInMilliseconds() - PAUSE_MARGIN;
1275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    if (delay < 0) {
1276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        delay = EMPTY_REQUEST_DELAY;
1277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
1278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    return delay;
1279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (BOSHException boshx) {
1281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.FINEST, "Could not extract", boshx);
1282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return getDefaultEmptyRequestDelay();
1286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Check the response for request acknowledgements and take appropriate
1290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * action.
1291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method assumes the lock is currently held.
1293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param req request
1295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param resp response
1296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void processRequestAcknowledgements(
1298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody req, final AbstractBody resp) {
1299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!cmParams.isAckingRequests()) {
1302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // If a report or time attribute is set, we aren't acking anything
1306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (resp.getAttribute(Attributes.REPORT) != null) {
1307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
1308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Figure out what the highest acked RID is
1311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String acked = resp.getAttribute(Attributes.ACK);
1312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Long ackUpTo;
1313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (acked == null) {
1314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Implicit ack of all prior requests up until RID
1315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            ackUpTo = Long.parseLong(req.getAttribute(Attributes.RID));
1316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
1317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            ackUpTo = Long.parseLong(acked);
1318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Remove the acked requests from the list
1321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (LOG.isLoggable(Level.FINEST)) {
1322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.finest("Removing pending acks up to: " + ackUpTo);
1323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Iterator<ComposableBody> iter = pendingRequestAcks.iterator();
1325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        while (iter.hasNext()) {
1326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            AbstractBody pending = iter.next();
1327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Long pendingRID = Long.parseLong(
1328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    pending.getAttribute(Attributes.RID));
1329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (pendingRID.compareTo(ackUpTo) <= 0) {
1330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                iter.remove();
1331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Process the response in order to update the response acknowlegement
1337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * data.
1338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method assumes the lock is currently held.
1340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param req request
1342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void processResponseAcknowledgementData(
1344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody req) {
1345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Long rid = Long.parseLong(req.getAttribute(Attributes.RID));
1348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (responseAck.equals(Long.valueOf(-1L))) {
1349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // This is the first request
1350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            responseAck = rid;
1351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } else {
1352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            pendingResponseAcks.add(rid);
1353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // Remove up until the first missing response (or end of queue)
1354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Long whileVal = responseAck;
1355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            while (whileVal.equals(pendingResponseAcks.first())) {
1356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                responseAck = whileVal;
1357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                pendingResponseAcks.remove(whileVal);
1358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                whileVal = Long.valueOf(whileVal.longValue() + 1);
1359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Process the response in order to check for and respond to any potential
1365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * ack reports.
1366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method assumes the lock is currently held.
1368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param resp response
1370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return exchange to transmit if a resend is to be performed, or
1371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *  {@code null} if no resend is necessary
1372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws BOSHException when a a retry is needed but cannot be performed
1373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private HTTPExchange processResponseAcknowledgementReport(
1375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final AbstractBody resp)
1376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throws BOSHException {
1377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertLocked();
1378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String reportStr = resp.getAttribute(Attributes.REPORT);
1380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (reportStr == null) {
1381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // No report on this message
1382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
1383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Long report = Long.parseLong(reportStr);
1386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Long time = Long.parseLong(resp.getAttribute(Attributes.TIME));
1387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (LOG.isLoggable(Level.FINE)) {
1388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            LOG.fine("Received report of missing request (RID="
1389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + report + ", time=" + time + "ms)");
1390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Find the missing request
1393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Iterator<ComposableBody> iter = pendingRequestAcks.iterator();
1394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        AbstractBody req = null;
1395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        while (iter.hasNext() && req == null) {
1396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            AbstractBody pending = iter.next();
1397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Long pendingRID = Long.parseLong(
1398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    pending.getAttribute(Attributes.RID));
1399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (report.equals(pendingRID)) {
1400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                req = pending;
1401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (req == null) {
1405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw(new BOSHException("Report of missing message with RID '"
1406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + reportStr
1407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    + "' but local copy of that request was not found"));
1408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Resend the missing request
1411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        HTTPExchange exch = new HTTPExchange(req);
1412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        exchanges.add(exch);
1413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        notEmpty.signalAll();
1414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return exch;
1415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Notifies all request listeners that the specified request is being
1419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * sent.
1420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param request request being sent
1422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void fireRequestSent(final AbstractBody request) {
1424d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1425d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1426d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        BOSHMessageEvent event = null;
1427d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (BOSHClientRequestListener listener : requestListeners) {
1428d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (event == null) {
1429d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                event = BOSHMessageEvent.createRequestSentEvent(this, request);
1430d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1431d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
1432d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                listener.requestSent(event);
1433d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (Exception ex) {
1434d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.WARNING, UNHANDLED, ex);
1435d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1436d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1437d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1438d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1439d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1440d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Notifies all response listeners that the specified response has been
1441d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * received.
1442d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1443d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param response response received
1444d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1445d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void fireResponseReceived(final AbstractBody response) {
1446d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1447d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1448d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        BOSHMessageEvent event = null;
1449d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (BOSHClientResponseListener listener : responseListeners) {
1450d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (event == null) {
1451d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                event = BOSHMessageEvent.createResponseReceivedEvent(
1452d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        this, response);
1453d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1454d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
1455d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                listener.responseReceived(event);
1456d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (Exception ex) {
1457d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.WARNING, UNHANDLED, ex);
1458d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1459d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1460d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1461d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1462d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1463d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Notifies all connection listeners that the session has been successfully
1464d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * established.
1465d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1466d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void fireConnectionEstablished() {
1467d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        final boolean hadLock = lock.isHeldByCurrentThread();
1468d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (hadLock) {
1469d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            lock.unlock();
1470d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1471d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
1472d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            BOSHClientConnEvent event = null;
1473d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (BOSHClientConnListener listener : connListeners) {
1474d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (event == null) {
1475d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    event = BOSHClientConnEvent
1476d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            .createConnectionEstablishedEvent(this);
1477d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1478d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
1479d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    listener.connectionEvent(event);
1480d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                } catch (Exception ex) {
1481d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    LOG.log(Level.WARNING, UNHANDLED, ex);
1482d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
1483d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1484d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
1485d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (hadLock) {
1486d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                lock.lock();
1487d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1488d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1489d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1490d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1491d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1492d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Notifies all connection listeners that the session has been
1493d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * terminated normally.
1494d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1495d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void fireConnectionClosed() {
1496d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1497d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1498d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        BOSHClientConnEvent event = null;
1499d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (BOSHClientConnListener listener : connListeners) {
1500d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (event == null) {
1501d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                event = BOSHClientConnEvent.createConnectionClosedEvent(this);
1502d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1503d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
1504d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                listener.connectionEvent(event);
1505d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (Exception ex) {
1506d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.WARNING, UNHANDLED, ex);
1507d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1508d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1509d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1510d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1511d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
1512d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Notifies all connection listeners that the session has been
1513d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * terminated due to the exceptional condition provided.
1514d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
1515d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param cause cause of the termination
1516d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
1517d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void fireConnectionClosedOnError(
1518d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            final Throwable cause) {
1519d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        assertUnlocked();
1520d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1521d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        BOSHClientConnEvent event = null;
1522d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (BOSHClientConnListener listener : connListeners) {
1523d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (event == null) {
1524d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                event = BOSHClientConnEvent
1525d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        .createConnectionClosedOnErrorEvent(
1526d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        this, pendingRequestAcks, cause);
1527d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1528d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
1529d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                listener.connectionEvent(event);
1530d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            } catch (Exception ex) {
1531d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                LOG.log(Level.WARNING, UNHANDLED, ex);
1532d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
1533d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
1534d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
1535d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
1536d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
1537