Skip to content

Instantly share code, notes, and snippets.

@sworisbreathing
Last active December 14, 2015 19:39
Show Gist options
  • Save sworisbreathing/5138350 to your computer and use it in GitHub Desktop.
Save sworisbreathing/5138350 to your computer and use it in GitHub Desktop.
PostQueryWithResponse is a utility class which can be used in an Apache Pivot application when it would be appropriate to use a GetQuery, but GetQuery cannot be used for practical reasons (i.e. URL/QueryString size limits).
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import org.apache.pivot.serialization.Serializer;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.web.Query;
import org.apache.pivot.web.QueryDictionary;
import org.apache.pivot.web.QueryException;
import org.apache.pivot.web.QueryListener;
/**
* A query which sends data via an HTTP POST request and expects a response
* body. This is intended to be used when it would be appropriate to use a GET
* request, but for practical reasons a GET request cannot be used (for example,
* when the request parameters would cause a GET query's URL to exceed the
* maximum length allowed by the server).
*
* @author Steven Swor
*/
public class PostQueryWithResponse extends Query<Object> {
/**
* The HTTP method used. ({@link Method#POST}).
*/
public static final Method METHOD = Method.POST;
/**
* The request value.
*/
private Object _postRequestValue = null;
/**
* Creates a new PostQueryWithResponse.
* @param hostname the host name
* @param path the path
* @see #PostQueryWithResponse(String, int, String, boolean)
*/
public PostQueryWithResponse(String hostname, String path) {
this(hostname, DEFAULT_PORT, path, false);
}
/**
* Creates a new PostQueryWithResponse.
* @param hostname the host name
* @param port the port
* @param path the path
* @param secure whether or not to use a secure connection
* @see #PostQueryWithResponse(String, int, String, boolean, ExecutorService)
*/
public PostQueryWithResponse(String hostname, int port, String path, boolean secure) {
this(hostname, port, path, secure, DEFAULT_EXECUTOR_SERVICE);
}
/**
* Creates a new PostQueryWithResponse.
* @param hostname the host name
* @param port the port
* @param path the path
* @param secure whether or not to use a secure connection
* @param executorService the service on which the request will be executed
* @see Query#Query(String, int, String, boolean, ExecutorService)
*/
public PostQueryWithResponse(String hostname, int port, String path, boolean secure,
ExecutorService executorService) {
super(hostname, port, path, secure, executorService);
}
/**
* Gets the post request value. This will be sent in the body of the POST
* request.
* @return the post request value
*/
public Object getPostRequestValue() {
return _postRequestValue;
}
/**
* Sets the post request value. This will be sent in the body of the POST
* request.
* @param postRequestValue the object which will be sent in the body of the
* POST request
*/
public void setPostRequestValue(Object postRequestValue) {
this._postRequestValue = postRequestValue;
}
/**
* Gets the method.
* @return Method#POST
*/
@Override
public Method getMethod() {
return METHOD;
}
@Override
public Object execute() throws QueryException {
return execute(METHOD, _postRequestValue);
}
/**
* The expected number of bytes in the server's response.
*/
private long _bytesExpected = -1;
/**
* Sets the expected number of bytes in the server's response.
* @param bytesExpected the expected number of bytes in the server's
* response
*/
protected void setBytesExpected(final long bytesExpected) {
this._bytesExpected = bytesExpected;
}
@Override
public long getBytesExpected() {
return _bytesExpected;
}
/**
* The response status.
*/
private int _status = 0;
/**
* Sets the response status.
* @param status the response status
*/
protected void setStatus(final int status) {
this._status = status;
}
@Override
public int getStatus() {
return this._status;
}
protected void setBytesSent(final long bytesSent) {
super.bytesSent = bytesSent;
}
protected void setBytesReceived(final long bytesReceived) {
super.bytesReceived = bytesReceived;
}
/**
* The response headers.
*/
protected QueryDictionary responseHeaders = super.getResponseHeaders();
@SuppressWarnings("unchecked")
@Override
protected Object execute(Method method, Object value) throws QueryException {
final URL location = getLocation();
final HttpURLConnection connection;
final Serializer<Object> serializer = (Serializer<Object>)getSerializer();
setBytesSent(0);
setBytesReceived(0);
setBytesExpected(-1);
setStatus(0);
String message = null;
final ListenerList<QueryListener<Object>> queryListeners = getQueryListeners();
try {
// Clear any properties from a previous response
getResponseHeaders().clear();
// Open a connection
final Proxy proxy = getProxy();
if (proxy == null) {
connection = (HttpURLConnection) location.openConnection();
} else {
connection = (HttpURLConnection) location.openConnection(proxy);
}
connection.setRequestMethod(method.toString());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(false);
connection.setUseCaches(false);
final HostnameVerifier hostnameVerifier = getHostnameVerifier();
if (connection instanceof HttpsURLConnection
&& hostnameVerifier != null) {
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
httpsConnection.setHostnameVerifier(hostnameVerifier);
}
// Set the request headers
if (value != null) {
connection.setRequestProperty("Content-Type", serializer.getMIMEType(value));
}
final QueryDictionary requestHeaders = getRequestHeaders();
for (String key : requestHeaders) {
for (int i = 0, n = requestHeaders.getLength(key); i < n; i++) {
if (i == 0) {
connection.setRequestProperty(key, requestHeaders.get(key, i));
} else {
connection.addRequestProperty(key, requestHeaders.get(key, i));
}
}
}
// Set the input/output state
connection.setDoInput(true);
connection.setDoOutput(value != null);
// Connect to the server
connection.connect();
notifyConnected(queryListeners);
// Write the request body
if (value != null) {
OutputStream outputStream = null;
try {
outputStream = connection.getOutputStream();
serializer.writeObject(value, new MonitoredOutputStream(outputStream));
} finally {
if (outputStream != null) {
outputStream.close();
}
}
}
// Notify listeners that the request has been sent
notifyRequestSent(queryListeners);
// Set the response info
int status = connection.getResponseCode();
setStatus(status);
message = connection.getResponseMessage();
// Record the content length
long bytesExpected = connection.getContentLength();
setBytesExpected(bytesExpected);
// NOTE Header indexes start at 1, not 0
int i = 1;
for (String key = connection.getHeaderFieldKey(i); key != null;
key = connection.getHeaderFieldKey(++i)) {
responseHeaders.add(key, connection.getHeaderField(i));
}
// If the response was anything other than 2xx, throw an exception
int statusPrefix = status / 100;
if (statusPrefix != 2) {
throw new QueryException(status, message);
}
Object result = null;
// Read the response body
if (status == Query.Status.OK) {
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
result = serializer.readObject(new MonitoredInputStream(inputStream));
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
// Notify listeners that the response has been received
notifyResponseReceived(queryListeners);
return result;
} catch (Exception exception) {
notifyFailed(queryListeners);
if (exception instanceof QueryException) {
throw ((QueryException)exception);
}else if (exception instanceof RuntimeException) {
throw ((RuntimeException)exception);
} else {
throw new QueryException(exception);
}
}
}
/**
* Notifies query listeners that the request failed.
* @param listeners the query listeners
*/
protected void notifyFailed(final ListenerList<QueryListener<Object>> listeners) {
synchronized(listeners) {
for (QueryListener<Object> listener : listeners) {
listener.failed(this);
}
}
}
/**
* Notifies query listeners that the server's response was received.
* @param listeners the query listeners
*/
protected void notifyResponseReceived(final ListenerList<QueryListener<Object>> listeners) {
synchronized(listeners) {
for (QueryListener<Object> listener : listeners) {
listener.responseReceived(this);
}
}
}
/**
* Notifies query listeners that the request was sent to the server.
* @param listeners the query listeners
*/
protected void notifyRequestSent(final ListenerList<QueryListener<Object>> listeners) {
synchronized(listeners) {
for (QueryListener<Object> listener : listeners) {
listener.requestSent(this);
}
}
}
/**
* Notifies query listeners that a connection was made to the server.
* @param listeners the query listeners
*/
protected void notifyConnected(final ListenerList<QueryListener<Object>> listeners) {
synchronized(listeners) {
for (QueryListener<Object> listener : listeners) {
listener.connected(this);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment