Я пытался установить конечную точку SOAP с Websocket в качестве транспортного протокола через CXF и реализовать вызов ее через CXF. Со встроенной пристанью. Я пробовал пару подходов, но ни один из подходов не сработал, к сожалению. Вот что я сделал:
Подход 1. В соответствии с документацией CXF веб-сокет поддерживается как транспортный протокол, и его поддержка предоставляется через
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-websocket</artifactId>
<version>3.3.2</version>
</dependency>
Я установил следующие зависимости:
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.0.39</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.3.2</version>
</dependency>
Код, который я выполняю, следующий:
Endpoint endpoint = Endpoint.create(new MyHelloWorldServicePortType() {
@Override
public String sayHello(HelloMessage message) throws FaultMessage {
return message.sayHello();
}
};
((org.apache.cxf.jaxws.EndpointImpl)endpoint).getFeatures().add(new
WSAddressingFeature());
endpoint.publish("ws://localhost:8088/MyHelloWorldService" );
URL wsdlDocumentLocation = new URL("file:/path to wsdl file");
String servicePart = "MyHelloWorldService";
String namespaceURI = "mynamespaceuri";
QName serviceQN = new QName(namespaceURI, servicePart);
Service service = Service.create(wsdlDocumentLocation, serviceQN);
MyHelloWorldServicePortType port = service.getPort( MyHelloWorldServicePortType.class);
portType.sayHello(new HelloMessage("Say Hello"));
Результат этого кода:
SEVERE: [ws] onError java.util.concurrent.TimeoutException: Request timeout to not-connected after 60000 ms at org.asynchttpclient.netty.timeout.TimeoutTimerTask.expire(TimeoutTimerTask.java:43) at org.asynchttpclient.netty.timeout.RequestTimeoutTimerTask.run(RequestTimeoutTimerTask.java:48) at io.netty.util.HashedWheelTimer$HashedWheelTimeout.expire(HashedWheelTimer.java:682) at io.netty.util.HashedWheelTimer$HashedWheelBucket.expireTimeouts(HashedWheelTimer.java:757) at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:485) at java.base/java.lang.Thread.run(Thread.java:834)
jun. 12, 2019 1:13:33 P.M. org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream connect SEVERE: unable to connect java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException: Request timeout to not-connected after 60000 ms at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999) at org.asynchttpclient.netty.NettyResponseFuture.get(NettyResponseFuture.java:172) at org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.connect(AhcWebSocketConduit.java:309) at org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.setupWrappedStream(AhcWebSocketConduit.java:167) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleHeadersTrustCaching(HTTPConduit.java:1343) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.onFirstWrite(HTTPConduit.java:1304) at org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:47) at org.apache.cxf.io.AbstractThresholdOutputStream.write(AbstractThresholdOutputStream.java:69) at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1356) at org.apache.cxf.transport.websocket.ahc.AhcWebSocketConduit$AhcWebSocketWrappedOutputStream.close(AhcWebSocketConduit.java:139) at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
Я абсолютно не понимаю, почему. Когда я пытаюсь подключиться через хром-клиент websocket по URL-адресу. Это говорит об успехе. При этом при подключении через клиент пишет Timeout.
Подход 2.
Я решил обмануть CXF и предоставить самодельную конечную точку Websocket, которая будет использоваться в качестве фронта для веб-сервиса CXF. Идея состоит в том, что клиент отправит сообщение через веб-сокет, сообщение будет развернуто, а затем отправлено через CXF. Этот подход очень похож на подход здесь, но здесь он использует JMS в качестве транспорта.
https://github.com/pbielicki/soap-websocket-cxf
Для этого я создал следующую точку доступа Websocket:
@ServerEndpoint("/jaxWSFront")
public class JaxWSFrontEnd {
@OnOpen
public void onOpen(final Session session) {
System.out.println("Hellooo");
}
@OnMessage
public void onMessage(String mySoapMessage,final Session session) throws Exception{
// The goal here is to get the soap message and redirect it via SOAP web //service. The JaxWSFacade acts as a point that understands websocket and then //gets the soap content and sends it to enpoint that understands SOAP.
session.getBasicRemote().sendText("Helllo . Now you see me.");
System.out.println("Hellooo again");
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("Hellooo");
}
@OnError
public void onError(Throwable t, Session session) {
System.out.println("Hellooo");
}
}
Теперь я указал свой клиентский прокси на jaxWsFrontEnd вместо конечной точки веб-службы. Я ожидаю, что я получу сообщение SOAP в методе onMessage, а затем смогу перенаправить SOAP в веб-службу CXF.
Теперь мой код выглядит так:
server = new Server(8088);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath( "/" );
server.setHandler(context);
ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
container.addEndpoint(JaxWSFrontEnd.class);
server.setHandler( context );
server.start();
Endpoint endpoint = Endpoint.create(new MyHelloWorldServicePortType() {
@Override
public String sayHello(HelloMessage message) throws FaultMessage {
return message.sayHello();
}
};
((org.apache.cxf.jaxws.EndpointImpl)endpoint).getFeatures().add(new
WSAddressingFeature());
URL wsdlDocumentLocation = new URL("file:/path to wsdl file");
String servicePart = "MyHelloWorldService";
String namespaceURI = "mynamespaceuri";
QName serviceQN = new QName(namespaceURI, servicePart);
Service service = Service.create(wsdlDocumentLocation, serviceQN);
MyHelloWorldServicePortType port = service.getPort( MyHelloWorldServicePortType.class);
portType.sayHello(new HelloMessage("Say Hello"));
Для второго подхода у меня были в дополнение к подходу 1 следующие зависимости:
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-common</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
Результат подхода 2 абсолютно такой же, как у подхода 1, исключения, которые я получаю, такие же, с одним небольшим отличием. Когда я использую клиент веб-сокета Chrome и указываю его непосредственно на jaxWsFrontend, я могу успешно отправить сообщение. Почему я не могу подключиться к веб-сокету через транспортные механизмы веб-сокета CXF???? Что я делаю неправильно ?
ОБНОВЛЕНИЕ: включение ведения журнала из NETTY. Похоже, что netty выдала ошибку java.lang.NoSuchMethodError: io.netty.channel.DefaultChannelId.newInstance()Lio/netty/channel/DefaultChannelId;
Возможно, у меня проблема с совместимостью версий с netty. Версия, которую я вижу, импортирована в проект — 4.1.33. Это транзитивная зависимость, которую я не декларировал.




Хорошо, мне действительно удалось взломать его в одиночку. Я опубликую ответ для завершения. По-видимому, ребята из CXF должны обновить свою документацию IMO. На их сайте указано, что для включения Websocket в качестве транспортного протокола нам нужно cxf-rt-транспорт-websocket зависимость.
Чего они не говорят, так это того, что вам дополнительно нужен асинхронный http-клиент не любой версии, а 2.0.39 довольно старой версии. Проблема в том, что он автоматически включает транзитивные зависимости к нетти 4.1 и начинает проявляться указанная выше ошибка. Что вам действительно нужно, так это nett 4.0.56
Вот фрагмент, который заставил меня работать:
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.0.39</version>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec-http</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.56.Final</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-websocket</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.3.2</version>
</dependency>
Подход 1 работает Подход 2. Мне удалось вызвать событие onConnect, тайм-аут onMessage, но, на мой взгляд, это должно сработать. Мне не хватает чего-то небольшого. В любом случае, у меня больше нет времени, и я доволен Aproach 1.