|
|
|
Using the Connection API
This topic describes using the Connection API to send a request to a remote resource using the connection infrastructure and obtain a response. For an overview of the connection infrastructure and associated topics, see Calling a remote resource.
You can find information about the protocols supplied for use with the Connection API in the Protocol reference. If you want to send email messages then you may also be interested in the additional support described in Sending an email using EmailConnection.
An overview of the Connection API
The Connection API is contained in the zero.core.connection package and has two key classes:
-
zero.core.connection.Connection
-
zero.core.connection.Connection.Response
An application uses the Connection class to build a request and send it to the named resource and is given a reference to a Connection.Response instance. It can then use the Connection.Response object to wait for and then examine the status and contents of the response.
The Connection class provides the application with two alternative approaches when initiating a request:
- A collection of
static methods, each of which set up initiate and send a request in a single method call.
- A more flexible API in which an application creates a
Connection object, sets up the request and sends it to the target resource in a series of method calls.
Using the static methods is equivalent to sending a simple request using the full Connection API and the way in which the application interacts with the Connection.Response object does not depend on the approach taken when sending the request.
The remainder of this article outlines these approaches in more detail. See the Javadoc API reference for more information.
Sending requests to resources
Sending requests using the static Connection API
The static methods of the Connection class provide a simple way to send REST-style requests to resources. The four basic forms are as follows:
| Connection |
Connection.Response doGET(String resource, Map<String, List<Object>> headers) |
Connection.Response doPUT(String resource, Map<String, List<Object>> headers, Object body) |
Connection.Response doPOST(String resource, Map<String, List<Object>> headers, Object body) |
Connection.Response doDELETE(String resource, Map<String, List<Object>> headers) |
In addition to these basic forms, the Connection class has overloaded forms that differ in the way the request headers are supplied.
- resource
- The URL or destination name of the resource to be invoked.
- headers
- The headers to be sent with the request expressed as a
Map of header names and List of associated values. If no request headers are required, the application may either pass an empty Map or an null value, or else use the overloaded methods that have no headers argument.
- body
- The body of the request. The supported body types will depend on the protocol being used but typically will include:
-
String
-
byte[]
-
InputStream
-
Reader
The meaning of request headers and the object types supported for the request body depend upon the protocol you are using. The Protocol reference contains more details. See the Javadoc API reference for more information on the methods described above.
The following code extract shows a simple example in which doGET() is used send an HTTP request to retrieve a resource:
try {
Connection.Response response = Connection.doGET("http://www.projectzero.org/repo");
// work with response headers and body
} catch (Exception e) {
// handle exceptions
}
Sending requests using the full Connection API
The static methods of the Connection class offer a simple way to issue requests and obtain a Connection.Response object. These methods are in fact a shorthand for the full Connection API, which may explicitly be used by applications in more advanced scenarios.
We can rewrite the example above to replace the single doGET() call with separate statements to create a new Connection object and get the associated Connection.Response object:
Connection.Response response = Connection.doGET("http://www.projectzero.org/repo");
Connection conn = new Connection("http://www.projectzero.org/repo");
Connection.Response response = conn.getResponse();
Applications using full Connection API follow the following pattern:
- Create a new
Connection object, specifying a target resource and operation.
- Set up any request headers required.
- Set the request body, if required.
- Send the request and obtain a reference to a
Connection.Response.
The Connection class provides two public constructors:
| Connection |
Connection(String resource) |
Connection(String resource, Connection.Operation operation) |
By default, the first form prepares a GET request but the second allows operation defined by the enumeration, Connection.Operation:
The newly created Connection object supplies a number of methods by which request headers can be set and are summarized below. Each header consists of a name (String key) and list of associated values (List<Object> values). The setRequestHeader() and removeRequestHeader() methods replace or remove all the values for the named header. However, the addRequestHeader() and addRequestHeaders() methods do not replace any existing header values. If the named headers are already set, these methods append the new values to the the end of the list.
| Connection |
void setRequestHeader(String key, List<Object> values) |
List<Object> removeRequestHeader(String key) |
void addRequestHeader(String key, Object value) |
void addRequestHeaders(Map<String, List<Object>> headers) |
The application may only call these methods before the request body has been set. Any subsequent attempts to call one of these methods will result in an IllegalStateException being thrown.
For POST and PUT operations, the request body is specified using one of two methods:
| Connection |
void setRequestBody(Object body) |
OutputStream getRequestBodyOutputStream() |
The application uses the setRequestBody() method to supply the message body in a similar way to static form of the Connection API. The supported body types will depend on the protocol being used but typically will include:
-
String
-
byte[]
-
InputStream
-
Reader
Alternatively, the getRequestBodyOutputStream() method allows an application to request an OutputStream in which it will write the request body. If this method is used to supply the request body, the application must flush() and close() the request body stream before the request is sent (i.e. before calling getResponse()).
The application may only call one of the methods to set the request body for an individual Connection object. Any subsequent attempts to call one of these methods will result in an IllegalStateException being thrown.
Once the request body is completed, the application uses the getResponse() to signal that the request should be sent to the target resource and obtain a reference to the Connection.Response object.
The following example code uses the full Connection API to prepare and send a simple email message:
Connection conn = new Connection("mailto:you@projectzero.org", Connection.Operation.POST);
conn.addRequestHeader("Subject", "Hello!");
conn.addRequestHeader("From", "me@projectzero.org");
conn.setRequestBody("This is the text of the email.");
Connection.Response resp = conn.getResponse();
Note that the EmailConnection class provides additional support for sending an email using the Connection API.
The meaning of request headers and the object types supported for the request body depend upon the protocol you are using. The Protocol reference contains more details. See the Javadoc API reference for more information on the methods described above.
Working with Connection.Response
After using Connection to send a request to a resource, the application is given a reference to a Connection.Response object. For protocols that supply a response to a request, an application may use the methods supplied by Connection.Response to:
- Examine any protocol-specific status code associated with the response.
- Examine any response headers.
- Obtain any response body.
Note: The application is given the Connection.Response reference after the request has been sent. Depending on the protocol in use, a response may not have been received from the resource at this time. If the response is still in-flight when the application eventually uses the methods of Connection.Response to examine it, the method call will block until the requested information is available.
Working with response headers
The following methods of Connection.Response allow an application to examine the headers supplied with the response:
| Connection.Response |
Set<String> getResponseHeaderKeys() |
List<Object> getResponseHeader(String key) |
Response header values are always represented as List objects, regardless of the protocol that has been used. The following code extract extends the doGET() example to display the first value of the response Content-Type header, if the header is present:
try {
Connection.Response response = Connection.doGET("http://www.projectzero.org/repo");
List<Object> contentType = resp.getResponseHeader("Content-Type");
if (contentType != null) {
System.out.println(contentType.get(0).toString());
}
// work with response body
} catch (Exception e) {
// handle exceptions
}
Obtaining the response body
Connection.Response provides several methods by which the application can obtain the response body. The basic forms are summarized below:
| Connection.Response |
String getResponseBodyAsString() |
byte[] getResponseBodyAsBytes() |
Reader getResponseBodyReader() |
InputStream getResponseBodyInputStream() |
Object getResponseBody() |
The application may only call one of the above methods, and only once, for an individual Connection.Response object. Any subsequent attempts to call one of these methods will result in an IllegalStateException being thrown.
The getResponseBody() method gives access to the response body object returned by the protocol implementation or any configured connection handlers. It is intended for use in advanced applications where such specific access is required. For general scenarios, it is recommended that applications use the method that converts the body into the form most suitable for the application. This both simplifies the logic and protects the application from implementation or configuration changes that may change the type of the body returned by getResponseBody().
The following extension of the doGET() example uses getResponseBodyAsString() to obtain the response body and writes it to System.out:
try {
Connection.Response response = Connection.doGET("http://www.projectzero.org/repo");
List<Object> contentType = response.getResponseHeader("Content-Type");
if (contentType != null) {
System.out.println(contentType.get(0).toString());
}
System.out.println(response.getResponseBodyAsString());
} catch (Exception e) {
// handle exceptions
}
The meaning of request headers and the object types supported for the request body depend upon the protocol you are using. The Protocol reference contains more details. See the Javadoc API reference for more information on the methods described above.
Advanced topics
Targeting local HTTP resources
The Connection API provides a convenient method to invoke a local HTTP resource without needing to specify a full URL. This mechanism may be used when the application invokes the Connection API whilst responding to an HTTP or HTTPS request.
When specifying a resource in any call to the Connection API, the application may use the following prefixes in place of an absolute URL.
| Prefix | Meaning |
./ | Relative to current resource. |
../ | Relative to parent resource. |
~/ | Relative to the application context root. |
For example, consider an application that is responding to a request against http://mydomain.com:8080/appRoot/level1/level2/level3. Using various forms of relative resource names will have the following results:
| Relative URL | Resolved resource |
./rel1 | http://mydomain.com:8080/appRoot/level1/level2/rel1 |
./rel1/rel2 | http://mydomain.com:8080/appRoot/level1/level2/rel1/rel2 |
../rel1 | http://mydomain.com:8080/appRoot/level1/rel1 |
~/rel1/rel2 | http://mydomain.com:8080/appRoot/rel1/rel2 |
Notes:
- Resource names that do not begin with the above prefixes are interpreted as absolute resource names. In particular, resources starting with
/ are not resolved relative to the current request.
- Calling
Connection.getResource() will always return the absolute URL for the Connection request, regardless of whether it was specified using an absolute name or relative prefix.
Sending an email
The EmailConnection API provides a specialized interface to help send a email with optional MIME attachments using the SMTP protocol. For more information, see Sending an email using the EmailConnection API.
|