Ми зіткнулися з тією ж проблемою з дещо іншою поведінкою. Для здійснення інших дзвінків ми використовували бібліотеку apache cxf. Для нас PATCH працював нормально, поки ми не розмовляли з нашими фальшивими службами, які працювали через http. У той момент, коли ми інтегрувались із реальними системами (які перевищували https), ми почали стикатися з тією ж проблемою з наступним трасуванням стека.
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]
Проблема відбувалася в цьому рядку коду
connection.setRequestMethod(httpRequestMethod); in URLConnectionHTTPConduit class of cxf library
Тепер справжня причина невдачі полягає в тому
java.net.HttpURLConnection contains a methods variable which looks like below
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
І ми бачимо, що не визначено жодного методу PATCH, отже помилка мала сенс. Ми спробували багато різних речей і переглянули переповнення стека. Єдиною розумною відповіддю було використання відображення для модифікації змінної методів для введення іншого значення "PATCH". Але якимось чином ми не були переконані використовувати це, оскільки рішення було свого роду хакерством і є занадто великою роботою і може мати вплив, оскільки у нас була спільна бібліотека для встановлення всіх зв’язків та виконання цих викликів REST.
Але потім ми зрозуміли, що бібліотека cxf обробляє виняток, і в блоці catch є код, який додає відсутній метод за допомогою відображення.
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;
}
}
Тепер це дало нам деякі сподівання, тому ми витратили трохи часу на читання коду і виявили, що якщо ми надаємо властивість для URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION, тоді ми можемо зробити cxf для запуску обробника винятків, і наша робота виконана, як за замовчуванням змінна буде присвоєне значення false через код нижче
DEFAULT_USE_REFLECTION =
Boolean.valueOf(SystemPropertyAction.getProperty(HTTPURL_CONNECTION_METHOD_REFLECTION, "false"));
Отже, ось що нам потрібно було зробити, щоб це працювало
WebClient.getConfig(client).getRequestContext().put("use.httpurlconnection.method.reflection", true);
або
WebClient.getConfig(client).getRequestContext().put(HTTPURL_CONNECTION_METHOD_REFLECTION, true);
Де WebClient - із самої бібліотеки cxf.
Сподіваюся, ця відповідь допоможе комусь.