From 41cfb9bbea63da4c098ea194c7ce49eb5b18580c Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Thu, 27 Oct 2011 18:50:52 +0000 Subject: [PATCH] Tweaked fluent API git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1189929 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 1 + .../http/client/fluent/FluentExecutor.java | 72 +++++++++++++++ .../http/client/fluent/FluentRequests.java | 64 +++++++++++++ .../client/fluent/FluentResponseHandling.java | 91 +++++++++++++++++++ .../apache/http/client/fluent/Content.java | 2 + .../apache/http/client/fluent/Executor.java | 12 +-- .../org/apache/http/client/fluent/Form.java | 57 ++++++++++++ .../apache/http/client/fluent/Request.java | 30 ++++-- .../apache/http/client/fluent/Response.java | 22 +++-- 9 files changed, 323 insertions(+), 28 deletions(-) create mode 100755 fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java create mode 100755 fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java create mode 100755 fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java create mode 100755 fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java diff --git a/.gitignore b/.gitignore index 50e1c490f..535b4cc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .project .settings .clover +.externalToolBuilders target diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java new file mode 100755 index 000000000..337fdcf3f --- /dev/null +++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java @@ -0,0 +1,72 @@ +/* + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.fluent; + +import java.io.File; + +import org.apache.http.HttpHost; +import org.apache.http.HttpVersion; +import org.apache.http.entity.ContentType; + +/** + * This example demonstrates how the he HttpClient fluent API can be used to execute multiple + * requests within the same security context. The Executor class maintains a common context shared + * by all requests executed with it. The Executor is thread-safe and can be used to execute + * requests concurrently from multiple threads of execution. + */ +public class FluentExecutor { + + public static void main(String[] args)throws Exception { + Executor executor = Executor.newInstance() + .auth(new HttpHost("somehost"), "username", "password") + .auth(new HttpHost("myproxy", 8080), "username", "password") + .authPreemptive(new HttpHost("myproxy", 8080)); + + // Execute a GET with timeout settings and return response content as String. + executor.execute(Request.Get("http://somehost/") + .connectTimeout(1000) + .socketTimeout(1000) + ).returnContent().asString(); + + // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1, + // containing a request body as String and return response content as byte array. + executor.execute(Request.Post("http://somehost/do-stuff") + .useExpectContinue() + .version(HttpVersion.HTTP_1_1) + .bodyString("Important stuff", ContentType.DEFAULT_TEXT) + ).returnContent().asBytes(); + + // Execute a POST with a custom header through the proxy containing a request body + // as an HTML form and save the result to the file + executor.execute(Request.Post("http://somehost/some-form") + .addHeader("X-Custom-header", "stuff") + .proxy(new HttpHost("myproxy", 8080)) + .bodyForm(Form.form().add("username", "vip").add("password", "secret").build()) + ).saveContent(new File("result.dump")); + } + +} diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java new file mode 100755 index 000000000..8271829f5 --- /dev/null +++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java @@ -0,0 +1,64 @@ +/* + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.fluent; + +import java.io.File; + +import org.apache.http.HttpHost; +import org.apache.http.HttpVersion; +import org.apache.http.entity.ContentType; + +/** + * This example demonstrates basics of request execution with the HttpClient fluent API. + */ +public class FluentRequests { + + public static void main(String[] args)throws Exception { + // Execute a GET with timeout settings and return response content as String. + Request.Get("http://somehost/") + .connectTimeout(1000) + .socketTimeout(1000) + .execute().returnContent().asString(); + + // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1, + // containing a request body as String and return response content as byte array. + Request.Post("http://somehost/do-stuff") + .useExpectContinue() + .version(HttpVersion.HTTP_1_1) + .bodyString("Important stuff", ContentType.DEFAULT_TEXT) + .execute().returnContent().asBytes(); + + // Execute a POST with a custom header through the proxy containing a request body + // as an HTML form and save the result to the file + Request.Post("http://somehost/some-form") + .addHeader("X-Custom-header", "stuff") + .proxy(new HttpHost("myproxy", 8080)) + .bodyForm(Form.form().add("username", "vip").add("password", "secret").build()) + .execute().saveContent(new File("result.dump")); + } + +} diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java new file mode 100755 index 000000000..70f18c1fe --- /dev/null +++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java @@ -0,0 +1,91 @@ +/* + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.fluent; + +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.StatusLine; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.HttpResponseException; +import org.apache.http.client.ResponseHandler; +import org.apache.http.entity.ContentType; +import org.apache.http.protocol.HTTP; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +/** + * This example demonstrates how the HttpClient fluent API can be used to handle HTTP responses + * without buffering content body in memory. + */ +public class FluentResponseHandling { + + public static void main(String[] args)throws Exception { + Document result = Request.Get("http://somehost/content") + .execute().handleResponse(new ResponseHandler() { + + public Document handleResponse(final HttpResponse response) throws IOException { + StatusLine statusLine = response.getStatusLine(); + HttpEntity entity = response.getEntity(); + if (statusLine.getStatusCode() >= 300) { + throw new HttpResponseException( + statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } + if (entity == null) { + throw new ClientProtocolException("Response contains no content"); + } + DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder docBuilder = dbfac.newDocumentBuilder(); + ContentType contentType = ContentType.getOrDefault(entity); + if (!contentType.equals(ContentType.APPLICATION_XML)) { + throw new ClientProtocolException("Unexpected content type:" + contentType); + } + String charset = contentType.getCharset(); + if (charset == null) { + charset = HTTP.DEFAULT_CONTENT_CHARSET; + } + return docBuilder.parse(entity.getContent(), charset); + } catch (ParserConfigurationException ex) { + throw new IllegalStateException(ex); + } catch (SAXException ex) { + throw new ClientProtocolException("Malformed XML document", ex); + } + } + + }); + // Do something useful with the result + System.out.println(result); + } + +} diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java index 2c80a9959..20d6d8c23 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java @@ -35,6 +35,8 @@ import org.apache.http.protocol.HTTP; public class Content { + public static final Content NO_CONTENT = new Content(new byte[] {}, ContentType.DEFAULT_BINARY); + private final byte[] raw; private final ContentType type; diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java index c2a4bc3ea..a4dfc99f1 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java @@ -87,8 +87,7 @@ public class Executor { return auth(authScope, creds); } - public Executor authPreemptive(final HttpHost host, final Credentials creds) { - auth(host, creds); + public Executor authPreemptive(final HttpHost host) { this.authCache.put(host, new BasicScheme()); return this; } @@ -117,13 +116,6 @@ public class Executor { return auth(host, new NTCredentials(username, password, workstation, domain)); } - public Executor authPreemptive(final HttpHost host, - final String username, final String password) { - auth(host, username, password); - this.authCache.put(host, new BasicScheme()); - return this; - } - public Executor clearAuth() { if (this.credentialsProvider != null) { this.credentialsProvider.clear(); @@ -143,7 +135,7 @@ public class Executor { return this; } - public Response exec( + public Response execute( final Request req) throws ClientProtocolException, IOException { this.localContext.setAttribute(ClientContext.CREDS_PROVIDER, this.credentialsProvider); this.localContext.setAttribute(ClientContext.AUTH_CACHE, this.authCache); diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java new file mode 100755 index 000000000..4d87d7fb3 --- /dev/null +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java @@ -0,0 +1,57 @@ +/* + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.client.fluent; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +public class Form { + + private final List params; + + public static Form form() { + return new Form(); + } + + Form() { + super(); + this.params = new ArrayList(); + } + + public Form add(final String name, final String value) { + this.params.add(new BasicNameValuePair(name, value)); + return this; + } + + public List build() { + return new ArrayList(this.params); + } + +} diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java index 6b0dcc1e0..ee81a911c 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java @@ -26,6 +26,7 @@ package org.apache.http.client.fluent; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -55,6 +56,7 @@ import org.apache.http.client.methods.HttpTrace; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; +import org.apache.http.entity.FileEntity; import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; import org.apache.http.params.CoreConnectionPNames; @@ -139,7 +141,7 @@ public class Request { return this.request; } - public Response exec() throws ClientProtocolException, IOException { + public Response execute() throws ClientProtocolException, IOException { return new Response(Executor.CLIENT.execute(this.request)); } @@ -268,35 +270,43 @@ public class Request { return this; } - public Request htmlFormBody(final NameValuePair[] formParams, final String charset) { + public Request bodyForm(final Iterable formParams, final String charset) { try { - return body(new UrlEncodedFormEntity(Arrays.asList(formParams))); + return body(new UrlEncodedFormEntity(formParams)); } catch (UnsupportedEncodingException ex) { throw new IllegalArgumentException(ex); } } - public Request htmlFormBody(final NameValuePair... formParams) { - return htmlFormBody(formParams, HTTP.DEFAULT_CONTENT_CHARSET); + public Request bodyForm(final Iterable formParams) { + return bodyForm(formParams, HTTP.DEFAULT_CONTENT_CHARSET); } - public Request stringBody(final String s, final ContentType contentType) { + public Request bodyForm(final NameValuePair... formParams) { + return bodyForm(Arrays.asList(formParams), HTTP.DEFAULT_CONTENT_CHARSET); + } + + public Request bodyString(final String s, final ContentType contentType) { return body(StringEntity.create(s, contentType)); } - public Request byteArrayBody(final byte[] b) { + public Request bodyFile(final File file, final ContentType contentType) { + return body(new FileEntity(file, contentType)); + } + + public Request bodyByteArray(final byte[] b) { return body(new ByteArrayEntity(b)); } - public Request byteArrayBody(final byte[] b, int off, int len) { + public Request bodyByteArray(final byte[] b, int off, int len) { return body(new ByteArrayEntity(b, off, len)); } - public Request streamBody(final InputStream instream) { + public Request bodyStream(final InputStream instream) { return body(new InputStreamEntity(instream, -1)); } - public Request streamBody(final InputStream instream, final ContentType contentType) { + public Request bodyStream(final InputStream instream, final ContentType contentType) { return body(new InputStreamEntity(instream, -1, contentType)); } diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java index 5cceeb9a7..a18222a93 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java @@ -56,7 +56,7 @@ public class Response { } } - public void dispose() { + private void dispose() { if (this.consumed) { return; } @@ -68,7 +68,12 @@ public class Response { } } - public T handle(final ResponseHandler handler) throws ClientProtocolException, IOException { + public void discardContent() { + dispose(); + } + + public T handleResponse( + final ResponseHandler handler) throws ClientProtocolException, IOException { assertNotConsumed(); try { return handler.handleResponse(this.response); @@ -77,8 +82,8 @@ public class Response { } } - public Content content() throws ClientProtocolException, IOException { - return handle(new ResponseHandler() { + public Content returnContent() throws ClientProtocolException, IOException { + return handleResponse(new ResponseHandler() { public Content handleResponse( final HttpResponse response) throws ClientProtocolException, IOException { @@ -89,17 +94,18 @@ public class Response { statusLine.getReasonPhrase()); } if (entity != null) { - return new Content(EntityUtils.toByteArray(entity), + return new Content( + EntityUtils.toByteArray(entity), ContentType.getOrDefault(entity)); } else { - return null; + return Content.NO_CONTENT; } } }); } - public HttpResponse response() throws IOException { + public HttpResponse returnResponse() throws IOException { assertNotConsumed(); try { HttpEntity entity = this.response.getEntity(); @@ -113,7 +119,7 @@ public class Response { } } - public void save(final File file) throws IOException { + public void saveContent(final File file) throws IOException { assertNotConsumed(); FileOutputStream out = new FileOutputStream(file); try {