Last active
December 14, 2015 19:39
-
-
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).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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