Hemos enfrentado el mismo problema con un comportamiento ligeramente diferente. Estábamos usando la biblioteca apache cxf para hacer el resto de llamadas. Para nosotros, PATCH funcionaba bien hasta que hablamos con nuestros servicios falsos que funcionaban a través de http. En el momento en que nos integramos con los sistemas reales (que estaban sobre https), comenzamos a enfrentar el mismo problema con el seguimiento de la pila siguiente.
java.net.ProtocolException: Invalid HTTP method: PATCH at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:428) ~[na:1.7.0_51] at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestMethod(HttpsURLConnectionImpl.java:374) ~[na:1.7.0_51] at org.apache.cxf.transport.http.URLConnectionHTTPConduit.setupConnection(URLConnectionHTTPConduit.java:149) ~[cxf-rt-transports-http-3.1.14.jar:3.1.14]
El problema estaba sucediendo en esta línea de código
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Ahora la verdadera razón del fracaso es que
java.net.HttpURLConnection contains a methods variable which looks like below
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
Y podemos ver que no hay un método PATCH definido, por lo que el error tiene sentido. Probamos muchas cosas diferentes y revisamos el desbordamiento de la pila. La única respuesta razonable fue utilizar la reflexión para modificar la variable de métodos para inyectar otro valor "PATCH". Pero de alguna manera no estábamos convencidos de usar eso, ya que la solución era una especie de pirateo y es demasiado trabajo y podría tener un impacto, ya que teníamos una biblioteca común para hacer todas las conexiones y realizar estas llamadas REST.
Pero luego nos dimos cuenta de que la biblioteca cxf en sí misma está manejando la excepción y hay un código escrito en el bloque catch para agregar el método que falta usando la reflexión.
try {
connection.setRequestMethod(httpRequestMethod);
} catch (java.net.ProtocolException ex) {
Object o = message.getContextualProperty(HTTPURL_CONNECTION_METHOD_REFLECTION);
boolean b = DEFAULT_USE_REFLECTION;
if (o != null) {
b = MessageUtils.isTrue(o);
}
if (b) {
try {
java.lang.reflect.Field f = ReflectionUtil.getDeclaredField(HttpURLConnection.class, "method");
if (connection instanceof HttpsURLConnection) {
try {
java.lang.reflect.Field f2 = ReflectionUtil.getDeclaredField(connection.getClass(),
"delegate");
Object c = ReflectionUtil.setAccessible(f2).get(connection);
if (c instanceof HttpURLConnection) {
ReflectionUtil.setAccessible(f).set(c, httpRequestMethod);
}
f2 = ReflectionUtil.getDeclaredField(c.getClass(), "httpsURLConnection");
HttpsURLConnection c2 = (HttpsURLConnection)ReflectionUtil.setAccessible(f2)
.get(c);
ReflectionUtil.setAccessible(f).set(c2, httpRequestMethod);
} catch (Throwable t) {
logStackTrace(t);
}
}
ReflectionUtil.setAccessible(f).set(connection, httpRequestMethod);
message.put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
} catch (Throwable t) {
logStackTrace(t);
throw ex;
}
}
Ahora, esto nos dio algunas esperanzas, así que dedicamos un tiempo a leer el código y descubrimos que si proporcionamos una propiedad para URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION, entonces podemos hacer que cxf ejecute el controlador de excepciones y nuestro trabajo se realiza ya que, de forma predeterminada, la variable será asignado a falso debido al código siguiente
DEFAULT_USE_REFLECTION =
Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Así que esto es lo que tuvimos que hacer para que esto funcione.
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
o
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Donde WebClient es de la propia biblioteca cxf.
Espero que esta respuesta ayude a alguien.