Springboot- oauth2 - websockets - Service Unavailable for async support - spring-boot

I have a springboot app , outh2 secure and now integrating websockets.
When I try connect , I get the error
Caused by:
org.springframework.web.socket.sockjs.SockJsTransportFailureException:
Failed to open session; nested exception is
java.lang.IllegalArgumentException: Async support must be enabled on a
servlet and for all filters involved in async request processing. This
is done in Java code using the Servlet API or by adding
"true" to servlet and filter
declarations in web.xml. Also you must use a Servlet 3.0+ container
As my app is springboot , and without web.xml. I added the below bean
#Bean
public ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new DispatcherServlet(), "/");
registration.setAsyncSupported(true);
return registration;
}
Now the new error is HTTP ERROR 503 Service Unavailable
The posts both didn't solve my problem
How to enable async support in a Springboot application packaged and deployed as WAR
Spring: Servlet 3.1 + SockJS = Async support must be enabled
How can I enable async and resolve my problem ?

Related

Deploying Spring Integration to WebSphere ND 8.5.5

I am looking for some guidance on deploying a simple Spring Integration application to WebSphere. The overall scope of the application is quite simple - it reads from a RabbitMQ endpoint, transforms any messages received to a specific xml format, and then posts the message to a JMS endpoint in WAS.
Initially, I built the application as a JAR. I was able to get it to work well enough with SSL turned off on the IIOP endpoints in WAS, but despite hours of debugging I never could get it to communicate properly with WAS over SSL. The initial handshake and communication with the bootstrap port was successful, but the SIB endpoint rejected the exact same certificate chain with the usual PKIX chaining error, and no amount of certificate importing made any difference.
So I elected to work out deploying the application as a web app into WAS itself, which would have been the end goal anyways. This caused a number of issues that i've had to work through:
I have not gotten properties to work in the normal Spring fashion. I assume that in this context Spring needs to be explicitly told where to look, but i've sidestepped this with hardcoding for now. Perhaps using #Resource annotations would be the way to do this in this context?
Various jar versioning issues, which i've mostly worked out by setting the application classloader as PARENT_LAST, and judiciously removing things that seemed redundant.
Oddly I did have to add some jars related to Parameter validation which don't seem to have been present in my original maven build.
Needing to set some values in the web.xml in order for spring to location configuration beans, specifically setting init-param with contextClass (org.springframework.web.context.support.AnnotationConfigWebApplicationContext) and contextConfigLocation set to a list of the objects that would normally be loaded via the #Configuration annotation.
May or may not be necessary but I did move from Maven to IID in order to hopefully avoid versioning issues with IBM related jars.
Now I would like to know if there are other items generally needed to be done to deploy Spring (especially Spring Integration) to WAS, and whether the above seems like enough.
In addition, I have an issue with the actual JMS connection to WAS. I have tried to use the UserCredentialsConnectionFactoryAdapter, and was successful with this with Spring standalone. However when deployed in WAS, an exception is thrown:
Caused by: java.lang.ClassCastException: com.ibm.ws.sib.api.jms.impl.JmsManagedQueueConnectionFactoryImpl incompatible with javax.jms.ConnectionFactory
I believe this is thrown when the setTargetConnectionFactory method is called, since if I use the connection factory without the UserCredentialsConnectionFactoryAdapter, it works fine, except the connection by "anonymous" is rejected by the bus:
[03/03/21 15:23:32:934 EST] 0000016c SibMessage W [BPM.WorkflowServer.Bus:Node1.server1-BPM.WorkflowServer.Bus] CWSII0212W: The bus BPM.WorkflowServer.Bus denied an anonymous user access to the bus.
If you want to see the code, this works fine (but doesn't authenticate):
#Bean
public ConnectionFactory jmsConnectionFactory() throws NamingException {
ConnectionFactory connectionFactory = null;
Context ctx = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
p.put(Context.PROVIDER_URL, providerUrl);
p.put(Context.SECURITY_AUTHENTICATION,"simple");
p.put(Context.SECURITY_PRINCIPAL,jmsUsername);
p.put(Context.SECURITY_CREDENTIALS,jmsPassword);
ctx = new InitialContext(p);
if (null != ctx)
System.out.println("Got naming context");
connectionFactory = (QueueConnectionFactory) ctx.lookup("javax.jms.QueueConnectionFactory");
if (null != connectionFactory)
System.out.println("Got connection factory");
return connectionFactory;
}
Whereas this throws the class cast exception:
#Bean
public UserCredentialsConnectionFactoryAdapter jmsConnectionFactory() throws NamingException {
ConnectionFactory connectionFactory = null;
Context ctx = null;
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
p.put(Context.PROVIDER_URL, providerUrl);
p.put(Context.SECURITY_AUTHENTICATION,"simple");
p.put(Context.SECURITY_PRINCIPAL,jmsUsername);
p.put(Context.SECURITY_CREDENTIALS,jmsPassword);
ctx = new InitialContext(p);
if (null != ctx)
System.out.println("Got naming context");
connectionFactory = (ConnectionFactory) ctx.lookup("javax.jms.QueueConnectionFactory");
if (null != connectionFactory)
System.out.println("Got connection factory");
UserCredentialsConnectionFactoryAdapter adapter = new UserCredentialsConnectionFactoryAdapter();
adapter.setTargetConnectionFactory(connectionFactory);
adapter.setUsername(jmsUsername);
adapter.setPassword(jmsPassword);
return adapter;
// return connectionFactory;
}
Note: the credentials set in the Context properties seem to have no effect.
I am using this connection factory with Spring Integration Java DSL:
.handle(Jms.outboundAdapter(jmsConfig.jmsConnectionFactory())
.destination(jmsDestination))
I understand from WebSphere documentation that supplying credentials happens on the ConnectionFactory.getConnection() call. So I wonder whether there is any hook in the DSL where I could override the getConnection so as to provide parameters and avoid the class cast exception that I am seeing.
Alternately I am considering just explicitly calling jms template methods to send the message using a lambda in the handler and creating the connection manually.
So, finally what I would like to ask for is:
Any overall guidance on deploying a Spring application to WebSphere traditional
What may be causing the class cast exception
ps, I have placed all of the spring, et al jars in a shared library. This is the contents:
c:/IBM/IID/sharedlibs/spring/accessors-smart-1.2.jar
c:/IBM/IID/sharedlibs/spring/amqp-client-5.10.0.jar
c:/IBM/IID/sharedlibs/spring/android-json-0.0.20131108.vaadin1.jar
c:/IBM/IID/sharedlibs/spring/apiguardian-api-1.1.0.jar
c:/IBM/IID/sharedlibs/spring/asm-5.0.4.jar
c:/IBM/IID/sharedlibs/spring/assertj-core-3.18.1.jar
c:/IBM/IID/sharedlibs/spring/byte-buddy-1.10.19.jar
c:/IBM/IID/sharedlibs/spring/byte-buddy-agent-1.10.19.jar
c:/IBM/IID/sharedlibs/spring/hamcrest-2.2.jar
c:/IBM/IID/sharedlibs/spring/hamcrest-core-2.2.jar
c:/IBM/IID/sharedlibs/spring/hamcrest-library-2.2.jar
c:/IBM/IID/sharedlibs/spring/http-client-3.8.0.RELEASE.jar
c:/IBM/IID/sharedlibs/spring/jackson-annotations-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-core-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-databind-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-dataformat-xml-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-datatype-jdk8-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-datatype-jsr310-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-module-jaxb-annotations-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jackson-module-parameter-names-2.11.4.jar
c:/IBM/IID/sharedlibs/spring/jakarta.activation-api-1.2.2.jar
c:/IBM/IID/sharedlibs/spring/jakarta.annotation-api-1.3.5.jar
c:/IBM/IID/sharedlibs/spring/jakarta.el-3.0.3.jar
c:/IBM/IID/sharedlibs/spring/json-path-2.4.0.jar
c:/IBM/IID/sharedlibs/spring/json-smart-2.3.jar
c:/IBM/IID/sharedlibs/spring/jsonassert-1.5.0.jar
c:/IBM/IID/sharedlibs/spring/objenesis-3.1.jar
c:/IBM/IID/sharedlibs/spring/reactive-streams-1.0.3.jar
c:/IBM/IID/sharedlibs/spring/reactor-core-3.4.2.jar
c:/IBM/IID/sharedlibs/spring/snakeyaml-1.27.jar
c:/IBM/IID/sharedlibs/spring/spring-amqp-2.3.4.jar
c:/IBM/IID/sharedlibs/spring/spring-aop-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-beans-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-autoconfigure-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-starter-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-starter-amqp-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-starter-json-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-starter-logging-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-boot-starter-web-2.4.2.jar
c:/IBM/IID/sharedlibs/spring/spring-context-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-core-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-expression-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-integration-amqp-5.4.3.jar
c:/IBM/IID/sharedlibs/spring/spring-integration-core-5.4.3.jar
c:/IBM/IID/sharedlibs/spring/spring-integration-jms-5.4.3.jar
c:/IBM/IID/sharedlibs/spring/spring-integration-xml-5.4.3.jar
c:/IBM/IID/sharedlibs/spring/spring-jcl-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-jms-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-messaging-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-oxm-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-rabbit-2.3.4.jar
c:/IBM/IID/sharedlibs/spring/spring-rabbit-junit-2.3.4.jar
c:/IBM/IID/sharedlibs/spring/spring-retry-1.3.1.jar
c:/IBM/IID/sharedlibs/spring/spring-tx-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-web-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-webmvc-5.3.3.jar
c:/IBM/IID/sharedlibs/spring/spring-xml-3.0.10.RELEASE.jar
c:/IBM/IID/sharedlibs/spring/stax2-api-4.2.1.jar
c:/IBM/IID/sharedlibs/spring/woodstox-core-6.2.4.jar
c:/IBM/IID/sharedlibs/spring/xmlunit-core-2.7.0.jar
c:/IBM/IID/sharedlibs/spring/slf4j-api-1.7.30.jar
c:/IBM/IID/sharedlibs/spring/jakarta.validation-api-2.0.2.jar
c:/IBM/IID/sharedlibs/spring/hibernate-validator-6.1.7.Final.jar
c:/IBM/IID/sharedlibs/spring/jboss-logging-3.4.1.Final.jar
c:/IBM/IID/sharedlibs/spring/classmate-1.5.1.jar
c:/IBM/IID/sharedlibs/spring/javax.jms-api-2.0.1.jar
UPDATE
So what I finally realized is that:
WAS 8.5.5 is using J2EE v6, which means JMS 1.1
Spring JMS is using JMS 2.0
When I switched to using the UserCredentialsConnectionFactoryAdapter, this tries to use the JmsContext interface which is part of JMS 2.0 classes, and not provided by the WAS jee container, so this was the reason for the class cast exception.
What I did was to do the JMS sending manually instead of using any spring integration gateway. A better solution might be to create my own adapter that extends connection factory and uses credentials in the connect method, but this works well enough for now:
.handle( m -> {
try {
jmsConfig.sendMessage( m.getPayload().toString() );
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} )
JmsConfig being a bean that manages the connection.

Metrics http_server_requests_seconds_count in spring boot application using cxf-spring-boot-starter-jaxrs contains uri as "UNKNOWN"

Metrics http_server_requests_seconds_count in Spring Boot application with version 2.0.8.Release exposed using spring actuator contains URI as:
"UNKNOWN".
Spring Boot application is using cxf-spring-boot-starter-jaxrs for exposing rest endpoints.
I have added micrometer-registry-prometheus dependency in my project.
http_server_requests_seconds_count{exception="None",method="POST",status="200",uri="UNKNOWN",} 2.0
I have tried adding micrometer-jersey2 dependency in my project.
Actual
http_server_requests_seconds_count{exception="None",method="POST",status="200",uri="UNKNOWN",} 2.0
Expected:
http_server_requests_seconds_count{exception="None",method="GET",status="200",uri="/sayHello",} 2.0
After the clarification in OP comments (CXF being another JAX-RS implementation): There's currently no support in Micrometer to handle CXF requests. It (Spring WebMvc) can't extract the optionally parameterized request url and in that case falls back to UNKNOWN. (Otherwise this could lead to a metrics explosion if your CXF endpoints provide some highly parameterizable URLs which get a lot of traffic.)
So you could have a look at the micrometer-jersey2 implementation and derive a micrometer-cxf implementation ;) (Or if not already the case (use the search) - open up an issue with the Micrometer or CXF project. I am mentioning the latter, because they might be interessted in taking care of that implementation.)
If you need cxf statistics for micrometer report, you can try
<dependency>
<groupId>io.github.kdprog</groupId>
<artifactId>cxf-micrometer-metrics</artifactId>
<version>1.0.0</version>
</dependency>
The following statistics will be reported
cxf_requests_processed_total - total number of cxf requests ,
cxf_requests_seconds_sum - total execution time of cxf requests,
cxf_requests_seconds_max - maximum execution time of cxf request,
cxf_requests_success_total - total number of successfully processed cxf requests,
cxf_requests_failed_total - total number of failed cxf requests
for each web service method of every client or server cxf endpoint.
For spring applications add the following bean to your application configuration.
#Bean
public FactoryBeanListener cxfMicrometerBean(final MeterRegistry registry) {
return new MicrometerFactoryBeanListener(registry);
}
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.supported
You can make custom tag provider to override it:
#Bean
WebMvcTagsProvider webMvcTagsProvider() {
return new DefaultWebMvcTagsProvider() {
#Override
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response,
Object handler, Throwable exception) {
return Tags.concat(
super.getTags(request, response, handler, exception),
Tags.of(Tag.of("uri",request.getRequestURI()))
);
}
};
}
More examples.
You can also collect cxf metrics for prometheus using io.github.ddk-prog:cxf-prometheus-metrics.
Add dependency to your pom.xml
<dependency>
<groupId>io.github.ddk-prog</groupId>
<artifactId>cxf-prometheus-metrics</artifactId>
<version>1.0.0</version>
</dependency>
and the following bean to your application configuration.
#Bean
public FactoryBeanListener cxfPrometheusFeatureBean(final CollectorRegistry registry) {
return new PrometheusFactoryBeanListener(registry);
}
You will get cxf_requests_total, cxf_requests_success, cxf_requests_failed, cxf_requests_seconds for each endpoint and operation in your spring boot actuator prometheus report.
For example,
cxf_requests_seconds{endpoint="server1",operation="server1Method",} 0.0157349
If you are using WebFlux on your project, you can make your custom tag provider by overriding:
#Bean
WebFluxTagsProvider webFluxTagsProvider() {
return new DefaultWebFluxTagsProvider() {
#Override
public Iterable<Tag> httpRequestTags(ServerWebExchange exchange, Throwable exception) {
return Tags.concat(super.httpRequestTags(exchange, exception), Tags.of(Tag.of("uri", exchange.getRequest().getPath().value())));
}
};
}
It works for me.

Spring cloud sleuth dependencies throwing RibbonLoadBalancerClientException in Spring boot stack application

We are planning to integrate spring sleuth in our Spring cloud microservices application. However when a Service A makes a call to Service B using RestTemplate we are getting RibbonLoadBalancerClient exception. Please find the stack trace below.
ResponseObject genericResponse = restTemplate.exchange(expandedUrl, HttpMethod.GET, new HttpEntity<>(null), ResponseObject.class).getBody();
Logger.log(DEBUG, "getDetails returned {} ", response);
return genericResponse.getData().getInformation();
The version are are using is as below
Spring Boot : springBootVersion = '1.5.3.RELEASE'
Spring Sleuth : spring-cloud-starter-sleuth:1.2.4.RELEASE
Spring cloud dependencies : spring-cloud-services dependencies:1.2.0.RELEASE,
spring-cloud-dependencies:Brixton.RELEASE
Exception - stacktrace :
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AbstractMethodError: org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(Ljava/lang/String;Lorg/springframework/cloud/client/ServiceInstance;Lorg/springframework/cloud/client/loadbalancer/LoadBalancerRequest;)Ljava/lang/Object;| at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:978)| at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)| at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)|
You should be using the release train to manage versions. Also Brixton is a very old release train. Please upgrade and don't set library versions manually.

java.net.UnknownHostException during Eureka service discovery

According to this blog https://spring.io/blog/2015/07/14/microservices-with-spring
Was able to run the application without any issues. In this order:
java -jar microservice-demo-0.0.1-SNAPSHOT.jar registration 1111
java -jar microservice-demo-0.0.1-SNAPSHOT.jar accounts 2222
java -jar microservice-demo-0.0.1-SNAPSHOT.jar web 3333
But when trying to hit any service through the web application (http://localhost:3333/) which uses the http://ACCOUNTS-SERVICE url to access any accounts service endpoints like http://ACCOUNTS-SERVICE/accounts/123456789 I'm getting an error response:
Response Status: 500 (Internal Server Error)
Cause: org.springframework.web.client.ResourceAccessException I/O error on GET request for "http://ACCOUNTS-SERVICE/accounts/123456789": ACCOUNTS-SERVICE; nested exception is java.net.UnknownHostException: ACCOUNTS-SERVICE
When I provide the real address (http://localhost:2223/) of the accounts service to the web server instead of the http://ACCOUNTS-SERVICE everything works properly but there is no service discovery in this case.
The source code is stored at: https://github.com/paulc4/microservices-demo
This issue was due to the RestTemplate was no longer auto-created in the Brixton release-train (Spring Cloud 1.1.0.RELEASE), so the RestTemplate could not resolve properly the http://ACCOUNTS-SERVICE url using the service discovery server.
Was able to fix this issue after declaring a RestTemplate bean with #LoadBalanced as follows:
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

Testing CXF and Jersey together causes Spring conflicts?

I have an app that uses CXF as a SOAP client and Jersey to present REST services, with the Jersey classes managed by Spring. This works fine running in Tomcat; however when attempting to test with JerseyTest I get Spring conflicts; it appears JerseyTest doesn't shut-down the Spring context correctly.
The test initialisation for Jersey looks like:
public MailProviderTest()
throws Exception
{
super(new WebAppDescriptor.Builder("net.haltcondition.service.rest")
.contextPath("")
.contextParam("contextConfigLocation", "classpath:applicationContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.build());
}
The CXF tests (which talk to our upstream provider's test servers) looks like:
#Before
public void setup()
{
// We need to do this the hard way to set the test endpoint rather than the default
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(Soap.class);
factory.setAddress("https://webservice.test.provider.com/Service.asmx");
soap = (Soap) factory.create();
Map<String,Object> outProps= new HashMap<String,Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, "TESTUSER");
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new WSAuthHandler("XXXX"));
Client cxfClient = ClientProxy.getClient(soap);
Endpoint cxfEndpoint = cxfClient.getEndpoint();
cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps));
}
When Maven runs the tests the Jersey class is run first; this results in the following error when running the CXF tests:
Caused by: java.lang.IllegalStateException: BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
at org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory(AbstractRefreshableApplicationContext.java:153)
at org.springframework.context.support.AbstractApplicationContext.containsBean(AbstractApplicationContext.java:892)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:143)
at org.apache.cxf.configuration.spring.ConfigurerImpl.configureBean(ConfigurerImpl.java:113)
at org.apache.cxf.transport.http.AbstractHTTPTransportFactory.configure(AbstractHTTPTransportFactory.java:228)
Unfortunately there doesn't seem to be any way to force the shutdown of the Spring application-context at the end of the Jersey tests and forcing per-test forks hasn't helped. It looks like I need to reset the Spring application-context as part of setting-up the CXF test, but I can't see how I would do this. Any pointers would be appreciated.

Resources