Решил написать статью, так как возился с этой проблемой несколько дней. Мы используем Axis2 для создания SOAP-сервисов. После обновления версии Axis2 до 1.7.9 все операции стали требовать обязательного присутствия HTTP-заголовка SOAPAction, что, разумеется, потенциально ломало все старые клиенты. Без указания этого заголовка возникала ошибка:
1 |
org.apache.axis2.AxisFault: The endpoint reference (EPR) for the Operation not found is http://mysite:9087/myService and the WSA Action = null |
Поиск по интернету ничего не дал. Я долго пытался включать и отключать различные Dispatcher-ы и опции в “axis2.xml”, но ничего не помогало. Перечитал всю документацию на официальном сайте, но тоже ничего не нашёл. Как же быть?
Раз уж сразу ничего не выходит, то подключаем тяжёлую артиллерию. Я выкачал исходники Axis2 и Axiom, подключил их к проекту и указал в Run Configurations, что исходники при отладке нужно искать и в них тоже. После пары часов отладки ситуация стала яснее.
Что же у нас происходит? Axis2 каждый запрос пропускает через серию Dispatcher-ов, описываемых в “axis2.xml”. Каждый из них работает с определённой частью запроса. Нас интересует SOAPMessageBodyBasedDispatcher, так как именно он в нашем случае должен из тела запроса узнавать, какая операция должна выполниться.
Обратите внимание на метод findOperation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public AxisOperation findOperation(AxisService service, MessageContext messageContext) throws AxisFault { String localName = messageContext.getEnvelope().getSOAPBodyFirstElementLocalName(); AxisOperation axisOperation = null; if (localName != null){ OMNamespace ns = messageContext.getEnvelope().getSOAPBodyFirstElementNS(); QName qName = null; if (ns != null) { qName = new QName(ns.getNamespaceURI(), localName); axisOperation = service.getOperationByMessageElementQName(qName); } // this is required for services uses the RPC message receiver if (axisOperation == null){ QName operationName = new QName(localName); axisOperation = service.getOperation(operationName); } } else { // Doc/Lit/Bare no arg messages can have an empty body. // See if any operations were registered with an empty body axisOperation = service.getOperationByMessageElementQName(null); } return axisOperation; } |
В самое первой строке метода он пытается получить имя первого элемента внутри body, у элемента envelope. Envelope и body — это стандартные элементы запроса SOAP. А дальше на основе полученного названия элемента определяется выполняемая операция. Однако, если вы в отладке зайдёте внутрь, то вы заметите, что происходит путаница, и метод getSOAPBodyFirstElementLocalName в реальности получает не первый дочерний элемент body, а сам envelope. А по названию “envelope” он, разумеется, никак не может определить операцию.
Явно какая-то ошибка. Поискал в Google и нашёл в трекере задач Axis2 точь в точь нашу ошибку. Да, это самая настоящая ошибка внутри Axis2. И она ещё не исправлена. Там же предлагается вариант обхода проблемы. Так вот. Проблема в том, что Axis2 может работать как с REST, так и с SOAP. В данном случае он ошибочно начинает считать, что наш запрос не SOAP, а REST, в результате возвращает нам самый первый элемент, минуя структуры запроса SOAP. Мы можем просто отключить поддержку REST в “axis2.xml” вот таким образом:
1 |
<parameter name="disableREST" locked="false">true</parameter> |
После этого можно отправлять все запросы так же, как и в старых версиях Axis2 без указания HTTP-заголовка SOAPAction.
Вам также может потребоваться избавиться от ошибки https is forbidden, которая у меня была до этого.