1diff --git com/kenai/jbosh/ApacheHTTPResponse.java com/kenai/jbosh/ApacheHTTPResponse.java 2new file mode 100644 3index 0000000..9f6731f 4--- /dev/null 5+++ com/kenai/jbosh/ApacheHTTPResponse.java 6@@ -0,0 +1,253 @@ 7+/* 8+ * Copyright 2009 Guenther Niess 9+ * 10+ * Licensed under the Apache License, Version 2.0 (the "License"); 11+ * you may not use this file except in compliance with the License. 12+ * You may obtain a copy of the License at 13+ * 14+ * http://www.apache.org/licenses/LICENSE-2.0 15+ * 16+ * Unless required by applicable law or agreed to in writing, software 17+ * distributed under the License is distributed on an "AS IS" BASIS, 18+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19+ * See the License for the specific language governing permissions and 20+ * limitations under the License. 21+ */ 22+ 23+package com.kenai.jbosh; 24+ 25+import java.io.IOException; 26+import java.util.concurrent.locks.Lock; 27+import java.util.concurrent.locks.ReentrantLock; 28+ 29+import org.apache.http.HttpEntity; 30+import org.apache.http.HttpResponse; 31+import org.apache.http.client.HttpClient; 32+import org.apache.http.client.methods.HttpPost; 33+import org.apache.http.entity.ByteArrayEntity; 34+ 35+import org.apache.http.protocol.BasicHttpContext; 36+import org.apache.http.protocol.HttpContext; 37+import org.apache.http.util.EntityUtils; 38+ 39+final class ApacheHTTPResponse implements HTTPResponse { 40+ 41+ /////////////////////////////////////////////////////////////////////////// 42+ // Constants: 43+ 44+ /** 45+ * Name of the accept encoding header. 46+ */ 47+ private static final String ACCEPT_ENCODING = "Accept-Encoding"; 48+ 49+ /** 50+ * Value to use for the ACCEPT_ENCODING header. 51+ */ 52+ private static final String ACCEPT_ENCODING_VAL = 53+ ZLIBCodec.getID() + ", " + GZIPCodec.getID(); 54+ 55+ /** 56+ * Name of the character set to encode the body to/from. 57+ */ 58+ private static final String CHARSET = "UTF-8"; 59+ 60+ /** 61+ * Content type to use when transmitting the body data. 62+ */ 63+ private static final String CONTENT_TYPE = "text/xml; charset=utf-8"; 64+ 65+ /////////////////////////////////////////////////////////////////////////// 66+ // Class variables: 67+ 68+ /** 69+ * Lock used for internal synchronization. 70+ */ 71+ private final Lock lock = new ReentrantLock(); 72+ 73+ /** 74+ * The execution state of an HTTP process. 75+ */ 76+ private final HttpContext context; 77+ 78+ /** 79+ * HttpClient instance to use to communicate. 80+ */ 81+ private final HttpClient client; 82+ 83+ /** 84+ * The HTTP POST request is sent to the server. 85+ */ 86+ private final HttpPost post; 87+ 88+ /** 89+ * A flag which indicates if the transmission was already done. 90+ */ 91+ private boolean sent; 92+ 93+ /** 94+ * Exception to throw when the response data is attempted to be accessed, 95+ * or {@code null} if no exception should be thrown. 96+ */ 97+ private BOSHException toThrow; 98+ 99+ /** 100+ * The response body which was received from the server or {@code null} 101+ * if that has not yet happened. 102+ */ 103+ private AbstractBody body; 104+ 105+ /** 106+ * The HTTP response status code. 107+ */ 108+ private int statusCode; 109+ 110+ /////////////////////////////////////////////////////////////////////////// 111+ // Constructors: 112+ 113+ /** 114+ * Create and send a new request to the upstream connection manager, 115+ * providing deferred access to the results to be returned. 116+ * 117+ * @param client client instance to use when sending the request 118+ * @param cfg client configuration 119+ * @param params connection manager parameters from the session creation 120+ * response, or {@code null} if the session has not yet been established 121+ * @param request body of the client request 122+ */ 123+ ApacheHTTPResponse( 124+ final HttpClient client, 125+ final BOSHClientConfig cfg, 126+ final CMSessionParams params, 127+ final AbstractBody request) { 128+ super(); 129+ this.client = client; 130+ this.context = new BasicHttpContext(); 131+ this.post = new HttpPost(cfg.getURI().toString()); 132+ this.sent = false; 133+ 134+ try { 135+ String xml = request.toXML(); 136+ byte[] data = xml.getBytes(CHARSET); 137+ 138+ String encoding = null; 139+ if (cfg.isCompressionEnabled() && params != null) { 140+ AttrAccept accept = params.getAccept(); 141+ if (accept != null) { 142+ if (accept.isAccepted(ZLIBCodec.getID())) { 143+ encoding = ZLIBCodec.getID(); 144+ data = ZLIBCodec.encode(data); 145+ } else if (accept.isAccepted(GZIPCodec.getID())) { 146+ encoding = GZIPCodec.getID(); 147+ data = GZIPCodec.encode(data); 148+ } 149+ } 150+ } 151+ 152+ ByteArrayEntity entity = new ByteArrayEntity(data); 153+ entity.setContentType(CONTENT_TYPE); 154+ if (encoding != null) { 155+ entity.setContentEncoding(encoding); 156+ } 157+ post.setEntity(entity); 158+ if (cfg.isCompressionEnabled()) { 159+ post.setHeader(ACCEPT_ENCODING, ACCEPT_ENCODING_VAL); 160+ } 161+ } catch (Exception e) { 162+ toThrow = new BOSHException("Could not generate request", e); 163+ } 164+ } 165+ 166+ /////////////////////////////////////////////////////////////////////////// 167+ // HTTPResponse interface methods: 168+ 169+ /** 170+ * Abort the client transmission and response processing. 171+ */ 172+ public void abort() { 173+ if (post != null) { 174+ post.abort(); 175+ toThrow = new BOSHException("HTTP request aborted"); 176+ } 177+ } 178+ 179+ /** 180+ * Wait for and then return the response body. 181+ * 182+ * @return body of the response 183+ * @throws InterruptedException if interrupted while awaiting the response 184+ * @throws BOSHException on communication failure 185+ */ 186+ public AbstractBody getBody() throws InterruptedException, BOSHException { 187+ if (toThrow != null) { 188+ throw(toThrow); 189+ } 190+ lock.lock(); 191+ try { 192+ if (!sent) { 193+ awaitResponse(); 194+ } 195+ } finally { 196+ lock.unlock(); 197+ } 198+ return body; 199+ } 200+ 201+ /** 202+ * Wait for and then return the response HTTP status code. 203+ * 204+ * @return HTTP status code of the response 205+ * @throws InterruptedException if interrupted while awaiting the response 206+ * @throws BOSHException on communication failure 207+ */ 208+ public int getHTTPStatus() throws InterruptedException, BOSHException { 209+ if (toThrow != null) { 210+ throw(toThrow); 211+ } 212+ lock.lock(); 213+ try { 214+ if (!sent) { 215+ awaitResponse(); 216+ } 217+ } finally { 218+ lock.unlock(); 219+ } 220+ return statusCode; 221+ } 222+ 223+ /////////////////////////////////////////////////////////////////////////// 224+ // Package-private methods: 225+ 226+ /** 227+ * Await the response, storing the result in the instance variables of 228+ * this class when they arrive. 229+ * 230+ * @throws InterruptedException if interrupted while awaiting the response 231+ * @throws BOSHException on communication failure 232+ */ 233+ private synchronized void awaitResponse() throws BOSHException { 234+ HttpEntity entity = null; 235+ try { 236+ HttpResponse httpResp = client.execute(post, context); 237+ entity = httpResp.getEntity(); 238+ byte[] data = EntityUtils.toByteArray(entity); 239+ String encoding = entity.getContentEncoding() != null ? 240+ entity.getContentEncoding().getValue() : 241+ null; 242+ if (ZLIBCodec.getID().equalsIgnoreCase(encoding)) { 243+ data = ZLIBCodec.decode(data); 244+ } else if (GZIPCodec.getID().equalsIgnoreCase(encoding)) { 245+ data = GZIPCodec.decode(data); 246+ } 247+ body = StaticBody.fromString(new String(data, CHARSET)); 248+ statusCode = httpResp.getStatusLine().getStatusCode(); 249+ sent = true; 250+ } catch (IOException iox) { 251+ abort(); 252+ toThrow = new BOSHException("Could not obtain response", iox); 253+ throw(toThrow); 254+ } catch (RuntimeException ex) { 255+ abort(); 256+ throw(ex); 257+ } 258+ } 259+} 260diff --git com/kenai/jbosh/ApacheHTTPSender.java com/kenai/jbosh/ApacheHTTPSender.java 261new file mode 100644 262index 0000000..2abb4ee 263--- /dev/null 264+++ com/kenai/jbosh/ApacheHTTPSender.java 265@@ -0,0 +1,156 @@ 266+/* 267+ * Copyright 2009 Guenther Niess 268+ * 269+ * Licensed under the Apache License, Version 2.0 (the "License"); 270+ * you may not use this file except in compliance with the License. 271+ * You may obtain a copy of the License at 272+ * 273+ * http://www.apache.org/licenses/LICENSE-2.0 274+ * 275+ * Unless required by applicable law or agreed to in writing, software 276+ * distributed under the License is distributed on an "AS IS" BASIS, 277+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 278+ * See the License for the specific language governing permissions and 279+ * limitations under the License. 280+ */ 281+ 282+package com.kenai.jbosh; 283+ 284+import java.util.concurrent.locks.Lock; 285+import java.util.concurrent.locks.ReentrantLock; 286+ 287+import org.apache.http.HttpHost; 288+import org.apache.http.HttpVersion; 289+import org.apache.http.client.HttpClient; 290+import org.apache.http.conn.ClientConnectionManager; 291+import org.apache.http.conn.params.ConnManagerParams; 292+import org.apache.http.conn.params.ConnRoutePNames; 293+import org.apache.http.conn.scheme.PlainSocketFactory; 294+import org.apache.http.conn.scheme.Scheme; 295+import org.apache.http.conn.scheme.SchemeRegistry; 296+import org.apache.http.conn.ssl.SSLSocketFactory; 297+import org.apache.http.impl.client.DefaultHttpClient; 298+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 299+import org.apache.http.params.BasicHttpParams; 300+import org.apache.http.params.HttpParams; 301+import org.apache.http.params.HttpProtocolParams; 302+ 303+/** 304+ * Implementation of the {@code HTTPSender} interface which uses the 305+ * Apache HttpClient API to send messages to the connection manager. 306+ */ 307+final class ApacheHTTPSender implements HTTPSender { 308+ 309+ /** 310+ * Lock used for internal synchronization. 311+ */ 312+ private final Lock lock = new ReentrantLock(); 313+ 314+ /** 315+ * Session configuration. 316+ */ 317+ private BOSHClientConfig cfg; 318+ 319+ /** 320+ * HttpClient instance to use to communicate. 321+ */ 322+ private HttpClient httpClient; 323+ 324+ /////////////////////////////////////////////////////////////////////////// 325+ // Constructors: 326+ 327+ /** 328+ * Prevent construction apart from our package. 329+ */ 330+ ApacheHTTPSender() { 331+ // Load Apache HTTP client class 332+ HttpClient.class.getName(); 333+ } 334+ 335+ /////////////////////////////////////////////////////////////////////////// 336+ // HTTPSender interface methods: 337+ 338+ /** 339+ * {@inheritDoc} 340+ */ 341+ public void init(final BOSHClientConfig session) { 342+ lock.lock(); 343+ try { 344+ cfg = session; 345+ httpClient = initHttpClient(session); 346+ } finally { 347+ lock.unlock(); 348+ } 349+ } 350+ 351+ /** 352+ * {@inheritDoc} 353+ */ 354+ public void destroy() { 355+ lock.lock(); 356+ try { 357+ if (httpClient != null) { 358+ httpClient.getConnectionManager().shutdown(); 359+ } 360+ } finally { 361+ cfg = null; 362+ httpClient = null; 363+ lock.unlock(); 364+ } 365+ } 366+ 367+ /** 368+ * {@inheritDoc} 369+ */ 370+ public HTTPResponse send( 371+ final CMSessionParams params, 372+ final AbstractBody body) { 373+ HttpClient mClient; 374+ BOSHClientConfig mCfg; 375+ lock.lock(); 376+ try { 377+ if (httpClient == null) { 378+ httpClient = initHttpClient(cfg); 379+ } 380+ mClient = httpClient; 381+ mCfg = cfg; 382+ } finally { 383+ lock.unlock(); 384+ } 385+ return new ApacheHTTPResponse(mClient, mCfg, params, body); 386+ } 387+ 388+ /////////////////////////////////////////////////////////////////////////// 389+ // Package-private methods: 390+ 391+ private synchronized HttpClient initHttpClient(final BOSHClientConfig config) { 392+ // Create and initialize HTTP parameters 393+ HttpParams params = new BasicHttpParams(); 394+ ConnManagerParams.setMaxTotalConnections(params, 100); 395+ HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 396+ HttpProtocolParams.setUseExpectContinue(params, false); 397+ if (config != null && 398+ config.getProxyHost() != null && 399+ config.getProxyPort() != 0) { 400+ HttpHost proxy = new HttpHost( 401+ config.getProxyHost(), 402+ config.getProxyPort()); 403+ params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); 404+ } 405+ 406+ // Create and initialize scheme registry 407+ SchemeRegistry schemeRegistry = new SchemeRegistry(); 408+ schemeRegistry.register( 409+ new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 410+ SSLSocketFactory sslFactory = SSLSocketFactory.getSocketFactory(); 411+ sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 412+ schemeRegistry.register( 413+ new Scheme("https", sslFactory, 443)); 414+ 415+ // Create an HttpClient with the ThreadSafeClientConnManager. 416+ // This connection manager must be used if more than one thread will 417+ // be using the HttpClient. 418+ ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry); 419+ return new DefaultHttpClient(cm, params); 420+ } 421+} 422diff --git com/kenai/jbosh/BodyParserXmlPull.java com/kenai/jbosh/BodyParserXmlPull.java 423index cc95236..5f23b06 100644 424--- com/kenai/jbosh/BodyParserXmlPull.java 425+++ com/kenai/jbosh/BodyParserXmlPull.java 426@@ -22,7 +22,6 @@ import java.lang.ref.SoftReference; 427 import java.util.logging.Level; 428 import java.util.logging.Logger; 429 import javax.xml.XMLConstants; 430-import javax.xml.namespace.QName; 431 import org.xmlpull.v1.XmlPullParser; 432 import org.xmlpull.v1.XmlPullParserException; 433 import org.xmlpull.v1.XmlPullParserFactory; 434diff --git com/kenai/jbosh/BodyQName.java com/kenai/jbosh/BodyQName.java 435index fc7ab0c..83acdf1 100644 436--- com/kenai/jbosh/BodyQName.java 437+++ com/kenai/jbosh/BodyQName.java 438@@ -16,8 +16,6 @@ 439 440 package com.kenai.jbosh; 441 442-import javax.xml.namespace.QName; 443- 444 /** 445 * Qualified name of an attribute of the wrapper element. This class is 446 * analagous to the {@code javax.xml.namespace.QName} class. 447