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