CXF RetryStrategy not working for Soap Webservice - spring

I'm using CXF for a SOAP webservice client and somehow, I'm unable to get a working RetryStrategy as a FailoverFeature. Another LoggingFeature is working fine. This is my Spring configuration:
#Bean
public MyPort myPort() {
final RetryStrategy retryStrategy = new RetryStrategy();
retryStrategy.setMaxNumberOfRetries(5);
retryStrategy.setDelayBetweenRetries(3000);
FailoverFeature failoverFeature = new FailoverFeature();
failoverFeature.setStrategy(retryStrategy);
failoverFeature.setTargetSelector(new FailoverTargetSelector(endpointAddress));
final LoggingFeature logFeature = new LoggingFeature();
MyService service = new MyService(WSDL_LOCATION, logFeature, failoverFeature);
MyPort port = service.getPort();
Client client = ClientProxy.getClient(port);
client.getRequestContext().put(ENDPOINT_ADDRESS, endpointAddress);
return port;
}
CXF seems to happily accept the FailoverFeature at boot time:
INFO org.apache.cxf.clustering.FailoverTargetSelector - corid= Using failover strategy org.apache.cxf.clustering.RetryStrategy#36931450
But a request like the following won't retry because I get a (intended) "502: Connection refused" after about 2sek.
myPort.doSomething()
What am I doing wrong?

My workaround is now to use Spring's retry mechanism:
#Retryable(
value = {HTTPException.class},
backoff = #Backoff(delay = 3000))
public void callWebservice() { ... }

You're creating a feature but not registering it. You need to add something like this:
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.getFeatures().add(failoverFeature);

Related

use spring boot data redis Connect to the redis cluster problem

I used spring boot data redis to connect to the redis cluster, using version 2.1.3 The configuration is as follows:
#Bean
#Primary
public RedisConnectionFactory myLettuceConnectionFactory(GenericObjectPoolConfig poolConfig) {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
final List<String> nodeList = redisProperties.getCluster().getNodes();
Set<RedisNode> nodes = new HashSet<RedisNode>();
for (String ipPort : nodeList) {
String[] ipAndPort = ipPort.split(":");
nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1])));
}
redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
redisClusterConfiguration.setClusterNodes(nodes);
redisClusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects());
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.commandTimeout(redisProperties.getTimeout())
.poolConfig(poolConfig)
.build();
RedisClusterClient clusterClient ;
LettuceConnectionFactory factory = new LettuceConnectionFactory(redisClusterConfiguration,clientConfig);
return factory;
}
However, during the operation, a WARN exception message will always be received as follows:
Well, this seems to be a problem with lettuce, How to map remote host & port to localhost using Lettuce,but I don't know how to use it in spring boot data redis. Any solution is welcome, thank you
I've got the answer, so let's define a ClinentRourse like this:
MappingSocketAddressResolver resolver = MappingSocketAddressResolver.create(DnsResolvers.UNRESOLVED ,
hostAndPort -> {
if(hostAndPort.getHostText().startsWith("172.31")){
return HostAndPort.of(ipStr, hostAndPort.getPort());
}
return hostAndPort;
});
ClientResources clientResources = ClientResources.builder()
.socketAddressResolver(resolver)
.build();
Then through LettuceClientConfiguration.clientResources method set in, the normal work of the lettuce.

Spring Jdbc inbound channel adapter

I'm trying for a program in spring which does DB poll and selects that record to read. I see example for xml but i would like to know how do we do in java config. Can someone show me an example ?
You need JdbcPollingChannelAdapter #Bean definition, marked with the #InboundChannelAdapter:
#Bean
#InboundChannelAdapter(value = "fooChannel", poller = #Poller(fixedDelay="5000"))
public MessageSource<?> storedProc(DataSource dataSource) {
return new JdbcPollingChannelAdapter(dataSource, "SELECT * FROM foo where status = 0");
}
http://docs.spring.io/spring-integration/docs/4.3.11.RELEASE/reference/html/overview.html#programming-tips

Feign with RibbonClient and Consul discovery without Spring Cloud

I was trying to setup Feign to work with RibbonClient, something like MyService api = Feign.builder().client(RibbonClient.create()).target(MyService.class, "https://myAppProd");, where myAppProd is an application which I can see in Consul. Now, if I use Spring annotations for the Feign client (#FeignClient("myAppProd"), #RequestMapping), everything works as Spring Cloud module will take care of everything.
If I want to use Feign.builder() and #RequestLine, I get the error:
com.netflix.client.ClientException: Load balancer does not have available server for client: myAppProd.
My first initial thought was that Feign was built to work with Eureka and only Spring Cloud makes the integration with Consul, but I am unsure about this.
So, is there a way to make Feign work with Consul without Spring Cloud?
Thanks in advance.
In my opinion, it's not feign work with consul, its feign -> ribbon -> consul.
RibbonClient needs to find myAppProd's serverList from its LoadBalancer.
Without ServerList, error: 'does not have available server for client'.
This job has been done by SpringCloudConsul and SpringCloudRibbon project, of course you can write another adaptor, it's just some glue code. IMHO, you can import this spring dependency into your project, but use it in non-spring way . Demo code:
just write a new feign.ribbon.LBClientFactory, that generate LBClient with ConsulServerList(Spring's class).
public class ConsulLBFactory implements LBClientFactory {
private ConsulClient client;
private ConsulDiscoveryProperties properties;
public ConsulLBFactory(ConsulClient client, ConsulDiscoveryProperties consulDiscoveryProperties) {
this.client = client;
this.properties = consulDiscoveryProperties;
}
#Override
public LBClient create(String clientName) {
IClientConfig config =
ClientFactory.getNamedConfig(clientName, DisableAutoRetriesByDefaultClientConfig.class);
ConsulServerList consulServerList = new ConsulServerList(this.client, properties);
consulServerList.initWithNiwsConfig(config);
ZoneAwareLoadBalancer<ConsulServer> lb = new ZoneAwareLoadBalancer<>(config);
lb.setServersList(consulServerList.getInitialListOfServers());
lb.setServerListImpl(consulServerList);
return LBClient.create(lb, config);
}
}
and then use it in feign:
public class Demo {
public static void main(String[] args) {
ConsulLBFactory consulLBFactory = new ConsulLBFactory(
new ConsulClient(),
new ConsulDiscoveryProperties(new InetUtils(new InetUtilsProperties()))
);
RibbonClient ribbonClient = RibbonClient.builder()
.lbClientFactory(consulLBFactory)
.build();
GitHub github = Feign.builder()
.client(ribbonClient)
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
List<Contributor> contributors = github.contributors("OpenFeign", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
interface GitHub {
#RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(#Param("owner") String owner, #Param("repo") String repo);
}
public static class Contributor {
String login;
int contributions;
}
}
you can find this demo code here, add api.github.com to your local consul before running this demo.

Websocket java client Spring + Stomp: Transport error: ConnectionLostException

I'm trying to create an standalone Java app as a websocket client using for this Spring with Stomp and Sockjs, taking in consideration the spring specifications and the spring-portafolio examples and I'm getting this error:
15:18:01.342 [main] DEBUG com.example.client.WebSocketClientTest - Connecting to : ws://localhost:8080/socket/hello
15:18:01.541 [WebSocketClient-AsyncIO-1] ERROR com.example.client.MyStompSessionHandler - Transport error
org.springframework.messaging.simp.stomp.ConnectionLostException: Connection closed
at org.springframework.messaging.simp.stomp.DefaultStompSession.afterConnectionClosed(DefaultStompSession.java:483) [spring-messaging-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.messaging.WebSocketStompClient$WebSocketTcpConnectionHandlerAdapter.afterConnectionClosed(WebSocketStompClient.java:354) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.sockjs.client.AbstractClientSockJsSession.afterTransportClosed(AbstractClientSockJsSession.java:321) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.sockjs.client.WebSocketTransport$ClientSockJsWebSocketHandler.afterConnectionClosed(WebSocketTransport.java:172) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onClose(StandardWebSocketHandlerAdapter.java:141) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]...
My code in the java client side is:
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(webSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
String stompUrl = "ws://localhost:8080/socket/hello";
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new StringMessageConverter());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setDefaultHeartbeat(new long[] {0, 0});
StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(stompUrl, sessionHandler);
stompClient.setTaskScheduler(taskScheduler);
And the server side was made using Spring MVC with Stomp and SockJs, the server works perfectly with a javascript client, this is the config that I used:
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello")
.setHandshakeHandler(new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy()))
.withSockJS();
}
What am I doing wrong? Can anyone give me an idea of how to fix it or how to create a Java client to connect with an spring websocket server?
Thanks in advance..
I had the same problem. Cause was recieving too big message which default STOMP Spring/Tomcat WS client cannot handle because it is not capable of partial messaging. This StackOverflow answer worked for me (I have set MAX_TEXT_MESSAGE_BUFFER_SIZE=20*1024*1024):
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.setDefaultMaxTextMessageBufferSize(MAX_TEXT_MESSAGE_BUFFER_SIZE);
WebSocketClient wsClient = new StandardWebSocketClient(container);
Setting inboundMessageSizeLimit had no effect:
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE);

Grizzly, sharing spring generated context

I have a standalone spring project and i need to start an embedded rest service with it.
I could be able to start the server with grizzly, my problem is, when i start grizzly server, it creates its own application context. so the instances created by my parent application is not accessible through the REST service.
Is there anyway of sharing the parent application's context between Grizzly server and parent application, other than getting grizzly generated application context.
This is my code for starting the grizzly server.
public class RemotingServer {
private HttpServer httpServer;
private String host;
private int port;
public RemotingServer(String host, int port) {
this.host = host;
this.port = port;
}
public void init() throws Exception {
URI uri = UriBuilder.fromUri("http://" + host + "/").port(port).build();
ResourceConfig rc = new DefaultResourceConfig();
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);
httpServer = GrizzlyServerFactory.createHttpServer(uri, rc, factory);
httpServer.start();
}
public void stop() {
httpServer.stop();
}
}
I tried setting current context as cac's parent too. Then i got following exception.
java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
thanks.
Try this:
ConfigurableApplicationContext cac =
new ClassPathXmlApplicationContext("classpath:remoting-context.xml");
// Have Spring load the context
cac.refresh();
IoCComponentProviderFactory factory = new SpringComponentProviderFactory(rc, cac);

Resources