If you're using HttpsUrlConnection
(note the 's') - then setting the HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
won't work. Keep reading for a detailed solution.
In my case, setting HttpUrlConnectorProvider.SET_METHOD_WORKAROUND
property caused a NoSuchFieldException
since my HttpUrlConnection
instance was actually of type: sun.net.www.protocol.https.HttpsURLConnectionImpl
and it's super: javax.net.ssl.HttpsURLConnection
(which inherits from HttpUrlConnection
).
So when Jackson code try to get the method field from my connection instance super (instance of javax.net.ssl.HttpsURLConnection
) here:
/**
* Workaround for a bug in {@code HttpURLConnection.setRequestMethod(String)}
* The implementation of Sun/Oracle is throwing a {@code ProtocolException}
* when the method is other than the HTTP/1.1 default methods. So to use {@code PROPFIND}
* and others, we must apply this workaround.
*
* See issue http://java.net/jira/browse/JERSEY-639
*/
private static void setRequestMethodViaJreBugWorkaround(final HttpURLConnection httpURLConnection, final String method) {
try {
httpURLConnection.setRequestMethod(method); // Check whether we are running on a buggy JRE
} catch (final ProtocolException pe) {
try {
final Class<?> httpURLConnectionClass = httpURLConnection.getClass();
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws NoSuchFieldException, IllegalAccessException {
final Field methodField = httpURLConnectionClass.getSuperclass().getDeclaredField("method");
methodField.setAccessible(true);
methodField.set(httpURLConnection, method);
return null;
}
});
} catch (final PrivilegedActionException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw new RuntimeException(cause);
}
}
}
}
We get a NoSuchFieldException
stating that a field named method
doesn't exist (since getDeclaredFields() brings all the fields, regardless of their accessibility but only for the current class, not any base classes that the current class might be inheriting from).
So I looked into Java's HttpUrlConnection code and saw that the allowed methods are specified by an private static String[]:
/* Adding PATCH to the valid HTTP methods */
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
The solution was to change this methods array using reflection:
try {
Field methodsField = HttpURLConnection.class.getDeclaredField("methods");
methodsField.setAccessible(true);
// get the methods field modifiers
Field modifiersField = Field.class.getDeclaredField("modifiers");
// bypass the "private" modifier
modifiersField.setAccessible(true);
// remove the "final" modifier
modifiersField.setInt(methodsField, methodsField.getModifiers() & ~Modifier.FINAL);
/* valid HTTP methods */
String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE", "PATCH"
};
// set the new methods - including patch
methodsField.set(null, methods);
} catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
Since methods field is static, changing its value works for any concrete instance that is extending HttpUrlConnection
including HttpsUrlConnection
.
Side note: I would prefer Java to add the PATCH method to the JDK or from Jackson to perform the field look up through the entire hierarchy in their workaround.
Anyway, I hope this solution will save you some time.