| diff --git com/kenai/jbosh/ApacheHTTPResponse.java com/kenai/jbosh/ApacheHTTPResponse.java |
| new file mode 100644 |
| index 0000000..9f6731f |
| --- /dev/null |
| +++ com/kenai/jbosh/ApacheHTTPResponse.java |
| @@ -0,0 +1,253 @@ |
| +/* |
| + * Copyright 2009 Guenther Niess |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +package com.kenai.jbosh; |
| + |
| +import java.io.IOException; |
| +import java.util.concurrent.locks.Lock; |
| +import java.util.concurrent.locks.ReentrantLock; |
| + |
| +import org.apache.http.HttpEntity; |
| +import org.apache.http.HttpResponse; |
| +import org.apache.http.client.HttpClient; |
| +import org.apache.http.client.methods.HttpPost; |
| +import org.apache.http.entity.ByteArrayEntity; |
| + |
| +import org.apache.http.protocol.BasicHttpContext; |
| +import org.apache.http.protocol.HttpContext; |
| +import org.apache.http.util.EntityUtils; |
| + |
| +final class ApacheHTTPResponse implements HTTPResponse { |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Constants: |
| + |
| + /** |
| + * Name of the accept encoding header. |
| + */ |
| + private static final String ACCEPT_ENCODING = "Accept-Encoding"; |
| + |
| + /** |
| + * Value to use for the ACCEPT_ENCODING header. |
| + */ |
| + private static final String ACCEPT_ENCODING_VAL = |
| + ZLIBCodec.getID() + ", " + GZIPCodec.getID(); |
| + |
| + /** |
| + * Name of the character set to encode the body to/from. |
| + */ |
| + private static final String CHARSET = "UTF-8"; |
| + |
| + /** |
| + * Content type to use when transmitting the body data. |
| + */ |
| + private static final String CONTENT_TYPE = "text/xml; charset=utf-8"; |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Class variables: |
| + |
| + /** |
| + * Lock used for internal synchronization. |
| + */ |
| + private final Lock lock = new ReentrantLock(); |
| + |
| + /** |
| + * The execution state of an HTTP process. |
| + */ |
| + private final HttpContext context; |
| + |
| + /** |
| + * HttpClient instance to use to communicate. |
| + */ |
| + private final HttpClient client; |
| + |
| + /** |
| + * The HTTP POST request is sent to the server. |
| + */ |
| + private final HttpPost post; |
| + |
| + /** |
| + * A flag which indicates if the transmission was already done. |
| + */ |
| + private boolean sent; |
| + |
| + /** |
| + * Exception to throw when the response data is attempted to be accessed, |
| + * or {@code null} if no exception should be thrown. |
| + */ |
| + private BOSHException toThrow; |
| + |
| + /** |
| + * The response body which was received from the server or {@code null} |
| + * if that has not yet happened. |
| + */ |
| + private AbstractBody body; |
| + |
| + /** |
| + * The HTTP response status code. |
| + */ |
| + private int statusCode; |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Constructors: |
| + |
| + /** |
| + * Create and send a new request to the upstream connection manager, |
| + * providing deferred access to the results to be returned. |
| + * |
| + * @param client client instance to use when sending the request |
| + * @param cfg client configuration |
| + * @param params connection manager parameters from the session creation |
| + * response, or {@code null} if the session has not yet been established |
| + * @param request body of the client request |
| + */ |
| + ApacheHTTPResponse( |
| + final HttpClient client, |
| + final BOSHClientConfig cfg, |
| + final CMSessionParams params, |
| + final AbstractBody request) { |
| + super(); |
| + this.client = client; |
| + this.context = new BasicHttpContext(); |
| + this.post = new HttpPost(cfg.getURI().toString()); |
| + this.sent = false; |
| + |
| + try { |
| + String xml = request.toXML(); |
| + byte[] data = xml.getBytes(CHARSET); |
| + |
| + String encoding = null; |
| + if (cfg.isCompressionEnabled() && params != null) { |
| + AttrAccept accept = params.getAccept(); |
| + if (accept != null) { |
| + if (accept.isAccepted(ZLIBCodec.getID())) { |
| + encoding = ZLIBCodec.getID(); |
| + data = ZLIBCodec.encode(data); |
| + } else if (accept.isAccepted(GZIPCodec.getID())) { |
| + encoding = GZIPCodec.getID(); |
| + data = GZIPCodec.encode(data); |
| + } |
| + } |
| + } |
| + |
| + ByteArrayEntity entity = new ByteArrayEntity(data); |
| + entity.setContentType(CONTENT_TYPE); |
| + if (encoding != null) { |
| + entity.setContentEncoding(encoding); |
| + } |
| + post.setEntity(entity); |
| + if (cfg.isCompressionEnabled()) { |
| + post.setHeader(ACCEPT_ENCODING, ACCEPT_ENCODING_VAL); |
| + } |
| + } catch (Exception e) { |
| + toThrow = new BOSHException("Could not generate request", e); |
| + } |
| + } |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // HTTPResponse interface methods: |
| + |
| + /** |
| + * Abort the client transmission and response processing. |
| + */ |
| + public void abort() { |
| + if (post != null) { |
| + post.abort(); |
| + toThrow = new BOSHException("HTTP request aborted"); |
| + } |
| + } |
| + |
| + /** |
| + * Wait for and then return the response body. |
| + * |
| + * @return body of the response |
| + * @throws InterruptedException if interrupted while awaiting the response |
| + * @throws BOSHException on communication failure |
| + */ |
| + public AbstractBody getBody() throws InterruptedException, BOSHException { |
| + if (toThrow != null) { |
| + throw(toThrow); |
| + } |
| + lock.lock(); |
| + try { |
| + if (!sent) { |
| + awaitResponse(); |
| + } |
| + } finally { |
| + lock.unlock(); |
| + } |
| + return body; |
| + } |
| + |
| + /** |
| + * Wait for and then return the response HTTP status code. |
| + * |
| + * @return HTTP status code of the response |
| + * @throws InterruptedException if interrupted while awaiting the response |
| + * @throws BOSHException on communication failure |
| + */ |
| + public int getHTTPStatus() throws InterruptedException, BOSHException { |
| + if (toThrow != null) { |
| + throw(toThrow); |
| + } |
| + lock.lock(); |
| + try { |
| + if (!sent) { |
| + awaitResponse(); |
| + } |
| + } finally { |
| + lock.unlock(); |
| + } |
| + return statusCode; |
| + } |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Package-private methods: |
| + |
| + /** |
| + * Await the response, storing the result in the instance variables of |
| + * this class when they arrive. |
| + * |
| + * @throws InterruptedException if interrupted while awaiting the response |
| + * @throws BOSHException on communication failure |
| + */ |
| + private synchronized void awaitResponse() throws BOSHException { |
| + HttpEntity entity = null; |
| + try { |
| + HttpResponse httpResp = client.execute(post, context); |
| + entity = httpResp.getEntity(); |
| + byte[] data = EntityUtils.toByteArray(entity); |
| + String encoding = entity.getContentEncoding() != null ? |
| + entity.getContentEncoding().getValue() : |
| + null; |
| + if (ZLIBCodec.getID().equalsIgnoreCase(encoding)) { |
| + data = ZLIBCodec.decode(data); |
| + } else if (GZIPCodec.getID().equalsIgnoreCase(encoding)) { |
| + data = GZIPCodec.decode(data); |
| + } |
| + body = StaticBody.fromString(new String(data, CHARSET)); |
| + statusCode = httpResp.getStatusLine().getStatusCode(); |
| + sent = true; |
| + } catch (IOException iox) { |
| + abort(); |
| + toThrow = new BOSHException("Could not obtain response", iox); |
| + throw(toThrow); |
| + } catch (RuntimeException ex) { |
| + abort(); |
| + throw(ex); |
| + } |
| + } |
| +} |
| diff --git com/kenai/jbosh/ApacheHTTPSender.java com/kenai/jbosh/ApacheHTTPSender.java |
| new file mode 100644 |
| index 0000000..2abb4ee |
| --- /dev/null |
| +++ com/kenai/jbosh/ApacheHTTPSender.java |
| @@ -0,0 +1,156 @@ |
| +/* |
| + * Copyright 2009 Guenther Niess |
| + * |
| + * Licensed under the Apache License, Version 2.0 (the "License"); |
| + * you may not use this file except in compliance with the License. |
| + * You may obtain a copy of the License at |
| + * |
| + * http://www.apache.org/licenses/LICENSE-2.0 |
| + * |
| + * Unless required by applicable law or agreed to in writing, software |
| + * distributed under the License is distributed on an "AS IS" BASIS, |
| + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| + * See the License for the specific language governing permissions and |
| + * limitations under the License. |
| + */ |
| + |
| +package com.kenai.jbosh; |
| + |
| +import java.util.concurrent.locks.Lock; |
| +import java.util.concurrent.locks.ReentrantLock; |
| + |
| +import org.apache.http.HttpHost; |
| +import org.apache.http.HttpVersion; |
| +import org.apache.http.client.HttpClient; |
| +import org.apache.http.conn.ClientConnectionManager; |
| +import org.apache.http.conn.params.ConnManagerParams; |
| +import org.apache.http.conn.params.ConnRoutePNames; |
| +import org.apache.http.conn.scheme.PlainSocketFactory; |
| +import org.apache.http.conn.scheme.Scheme; |
| +import org.apache.http.conn.scheme.SchemeRegistry; |
| +import org.apache.http.conn.ssl.SSLSocketFactory; |
| +import org.apache.http.impl.client.DefaultHttpClient; |
| +import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; |
| +import org.apache.http.params.BasicHttpParams; |
| +import org.apache.http.params.HttpParams; |
| +import org.apache.http.params.HttpProtocolParams; |
| + |
| +/** |
| + * Implementation of the {@code HTTPSender} interface which uses the |
| + * Apache HttpClient API to send messages to the connection manager. |
| + */ |
| +final class ApacheHTTPSender implements HTTPSender { |
| + |
| + /** |
| + * Lock used for internal synchronization. |
| + */ |
| + private final Lock lock = new ReentrantLock(); |
| + |
| + /** |
| + * Session configuration. |
| + */ |
| + private BOSHClientConfig cfg; |
| + |
| + /** |
| + * HttpClient instance to use to communicate. |
| + */ |
| + private HttpClient httpClient; |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Constructors: |
| + |
| + /** |
| + * Prevent construction apart from our package. |
| + */ |
| + ApacheHTTPSender() { |
| + // Load Apache HTTP client class |
| + HttpClient.class.getName(); |
| + } |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // HTTPSender interface methods: |
| + |
| + /** |
| + * {@inheritDoc} |
| + */ |
| + public void init(final BOSHClientConfig session) { |
| + lock.lock(); |
| + try { |
| + cfg = session; |
| + httpClient = initHttpClient(session); |
| + } finally { |
| + lock.unlock(); |
| + } |
| + } |
| + |
| + /** |
| + * {@inheritDoc} |
| + */ |
| + public void destroy() { |
| + lock.lock(); |
| + try { |
| + if (httpClient != null) { |
| + httpClient.getConnectionManager().shutdown(); |
| + } |
| + } finally { |
| + cfg = null; |
| + httpClient = null; |
| + lock.unlock(); |
| + } |
| + } |
| + |
| + /** |
| + * {@inheritDoc} |
| + */ |
| + public HTTPResponse send( |
| + final CMSessionParams params, |
| + final AbstractBody body) { |
| + HttpClient mClient; |
| + BOSHClientConfig mCfg; |
| + lock.lock(); |
| + try { |
| + if (httpClient == null) { |
| + httpClient = initHttpClient(cfg); |
| + } |
| + mClient = httpClient; |
| + mCfg = cfg; |
| + } finally { |
| + lock.unlock(); |
| + } |
| + return new ApacheHTTPResponse(mClient, mCfg, params, body); |
| + } |
| + |
| + /////////////////////////////////////////////////////////////////////////// |
| + // Package-private methods: |
| + |
| + private synchronized HttpClient initHttpClient(final BOSHClientConfig config) { |
| + // Create and initialize HTTP parameters |
| + HttpParams params = new BasicHttpParams(); |
| + ConnManagerParams.setMaxTotalConnections(params, 100); |
| + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); |
| + HttpProtocolParams.setUseExpectContinue(params, false); |
| + if (config != null && |
| + config.getProxyHost() != null && |
| + config.getProxyPort() != 0) { |
| + HttpHost proxy = new HttpHost( |
| + config.getProxyHost(), |
| + config.getProxyPort()); |
| + params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); |
| + } |
| + |
| + // Create and initialize scheme registry |
| + SchemeRegistry schemeRegistry = new SchemeRegistry(); |
| + schemeRegistry.register( |
| + new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); |
| + SSLSocketFactory sslFactory = SSLSocketFactory.getSocketFactory(); |
| + sslFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); |
| + schemeRegistry.register( |
| + new Scheme("https", sslFactory, 443)); |
| + |
| + // Create an HttpClient with the ThreadSafeClientConnManager. |
| + // This connection manager must be used if more than one thread will |
| + // be using the HttpClient. |
| + ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry); |
| + return new DefaultHttpClient(cm, params); |
| + } |
| +} |
| diff --git com/kenai/jbosh/BodyParserXmlPull.java com/kenai/jbosh/BodyParserXmlPull.java |
| index cc95236..5f23b06 100644 |
| --- com/kenai/jbosh/BodyParserXmlPull.java |
| +++ com/kenai/jbosh/BodyParserXmlPull.java |
| @@ -22,7 +22,6 @@ import java.lang.ref.SoftReference; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.xml.XMLConstants; |
| -import javax.xml.namespace.QName; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlPullParserFactory; |
| diff --git com/kenai/jbosh/BodyQName.java com/kenai/jbosh/BodyQName.java |
| index fc7ab0c..83acdf1 100644 |
| --- com/kenai/jbosh/BodyQName.java |
| +++ com/kenai/jbosh/BodyQName.java |
| @@ -16,8 +16,6 @@ |
| |
| package com.kenai.jbosh; |
| |
| -import javax.xml.namespace.QName; |
| - |
| /** |
| * Qualified name of an attribute of the wrapper element. This class is |
| * analagous to the {@code javax.xml.namespace.QName} class. |