Using CXF with Spring Boot Actuator - servlets

I am working on a web service host application in which using cxf with spring boot. when I register cxf servlet with following code web service side works and I can see published wsdls.
However after setting cxf servlet Spring boot actuator and rest endpoints not working and returning 404. How can I solve this issue ?
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}

Although I dont know the reason, when I set a name like below it starts working.
#Bean
public ServletRegistrationBean cxfServlet() {
ServletRegistrationBean cxf = new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
cxf.setName("cxfServlet");
return cxf;
}

Here is simple spring boot configuration I use.
#Configuration
#Import(value = { JaxRsConfig.class })
public class CxfRestConfig {
#Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
}
#Component
public class CustomSpringComponentScanServer
extends AbstractSpringComponentScanServer {
#Override
protected String getAddress() {
return "/api";
}
#Bean
public Server jaxRsServer() {
super.getFeatures().add(new LoggingFeature());
return super.createJaxRsServer();
}
}
}
Note: With ComponentScanner you need to annotate your service class with Spring annotations along with #Path Annotation at class level.
If you do not want list of apis in http://localhost:8080/cxf you can directly remove the custom class I had written and you can import directly as shown below.
#Import(value = { JaxRsConfig.class, SpringComponentScanServer.class })

I was getting the same problem with Kotlin and this post indirectly helped me. My code was like this
#Bean
fun dispatcherServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
After changing the method name from dispatcherServlet to cxfServlet the actuator magically started to work.
#Bean
fun cxfServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
I guess it was conflicting with some Spring default servlet.

I looks like there is a clash between servlets.
You can check it in your logs. There should be:
2017-04-01 15:34:04,029 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'CXFServlet' to [/soap-api/*]
2017-04-01 15:34:04,031 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/]
There should be exactly two servlets and the path should be different.
If there is one missing the enpoints won't work.
dispatcherServlet is spring default one to handle actuator metrics

Related

How can I invoke the health check of another Spring Boot application from within my Spring Boot health endpoint?

I wonder how I can invoke a custom health indicator:
in the same application
of another Spring Boot application
My application is split into a base application (rather a configuration) A which implements nearly all the functionality (having no main method) and another application B (having a main method ;-) ) having the base configuration as a dependency in the POM.
In A I have implemented a custom HealthIndicator:
#Component
#RequiredArgsConstructor
public class AdapterDownstreamHealthIndicator implements HealthIndicator {
private RestTemplate restTemplate;
private String downStreamUrl = "http://localhost:8081/actuator";
public AdapterDownstreamHealthIndicator(RestTemplate restTemplate, String downStreamUrl) {
this.restTemplate = restTemplate;
this.downStreamUrl = downStreamUrl;
}
#Override
public Health health() {
// try {
// JsonNode resp = restTemplate.getForObject(downStreamUrl + "/health", JsonNode.class);
// if (resp.get("status").asText().equalsIgnoreCase("UP")) {
// System.out.println("JUHUUUUUUUUUUU!!!!");
// return Health.up().build();
// }
// } catch (Exception ex) {
// return Health.down(ex).build();
// }
return Health.down().build();
}
}
In my application.properties I have some actuator properties:
management.endpoints.web.exposure.include=health,info,prometheus,adapterDownstream
spring.jackson.serialization.INDENT_OUTPUT=true
management.endpoint.health.show-details=always
When I enter http://localhost:9091/actuator/health/adapterDownstream in a browser the debugger does not stop in the health() method and I simply get an empty page displayed.
I already tried to extend AbstractHealthIndicator instead of implementing HealthIndicator interface.
What am I doing wrong that the custom health indicator is not recognized?
In the end I want to make some kind of deep health check to test all components being used in my application. Maybe using CompositeHealthContributor should be used???
As I described I have a dependency A which has NO main method which is loaded into my application B as a dependency in the POM. So far I tried to implement the custom healthcheck class/the health indicator in this dependency/module A.
The simple solution is to add a
#ComponentScan(basePackages = "path.to.actuator") to the main method of the application.

FeignClients get published as REST endpoints in spring cloud application

I've got REST FeignClient defined in my application:
#FeignClient(name = "gateway", configuration = FeignAuthConfig.class)
public interface AccountsClient extends Accounts {
}
I share endpoint interface between server and client:
#RequestMapping(API_PATH)
public interface Accounts {
#PostMapping(path = "/register",
produces = APPLICATION_JSON_VALUE,
consumes = APPLICATION_JSON_VALUE)
ResponseEntity<?> registerAccount(#RequestBody ManagedPassUserVM managedUserDTO)
throws EmailAlreadyInUseException, UsernameAlreadyInUseException, URISyntaxException;
}
Everythng works fine except that my FeignClient definition in my client application also got registered as independent REST endpoint.
At the moment I try to prevent this behavior using filter which returns 404 status code for FeignClinet client mappings in my client application. However this workeraund seems very inelegant.
Is there another way how to prevent feign clients registering as separate REST endpoints?
It is a known limitation of Spring Cloud's feign support. By adding #RequestMapping to the interface, Spring MVC (not Spring Cloud) assumes you want as an endpoint. #RequestMapping on Feign interfaces is not currently supported.
I've used workaround for this faulty Spring Framework behavior:
#Configuration
#ConditionalOnClass({Feign.class})
public class FeignMappingDefaultConfiguration {
#Bean
public WebMvcRegistrations feignWebRegistrations() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new FeignFilterRequestMappingHandlerMapping();
}
};
}
private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
#Override
protected boolean isHandler(Class<?> beanType) {
return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, FeignClient.class) == null);
}
}
}
I found it in SpringCloud issue

Spring Cloud Netflix : Passing host request parameter via RequestInterceptor to FeignClient

I am building a Spring Cloud project (Brixton.M4 with Spring Boot 1.3.1) with Eureka, Zuul and FeignClient where I am trying to add multi tenancy support (Tenants are identified by subdomain : tenant1.myservice.com). To do so, I would like to somehow pass the original subdomain along requests that are forwarded from a service to the other via Feign but I can't seem to be able to find the right way to do it.
What I have is a client that exposes a #RestController which calls a #FeignClient to communicate with my backend which exposes server operations to the client through its own #RestController.
The #FeignClient using same interface as my #RestController on the server :
#FeignClient(name = "product")
public interface ProductService extends IProductService {
}
What I am currently trying to do is set a header in a RequestInterceptor :
#Component
public class MultiTenancyRequestInterceptor implements RequestInterceptor {
private CurrentTenantProvider currentTenantProvider;
#Autowired
public MultiTenancyRequestInterceptor(CurrentTenantProvider currentTenantProvider) {
this.currentTenantProvider = currentTenantProvider;
}
#Override
public void apply(RequestTemplate template) {
try {
template.header("TENANT", currentTenantProvider.getTenant());
} catch (Exception e) {
// "oops"
}
}
}
My provider class is a simple component where I'm trying to inject a request / session scope bean :
#Component
public class CurrentTenantProvider {
#Autowired
private CurrentTenant currentTenant;
//...
}
The bean (I tried both session and request scope) :
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public CurrentTenant currentTenant() {
return new CurrentTenant();
}
On the server, I use Hibernate multitenant provider that is supposed to catch the header value and use it to define which DB to connect to :
#Autowired
private HttpServletRequest httpRequest;
#Override
public String resolveCurrentTenantIdentifier() {
return httpRequest.getHeader("TENANT");
}
It seems the Feign call to the server is done in another thread and out of the incoming request scope, so i'm not sure how to pass that value along.
It all works fine when I hardcode the tenant value in the RequestInterceptor so I know the rest is working properly.
I have also looked at many other posts about Zuul "X-Forwaded-For" header and cannot find it in the request received on the server. I have also tried adding a ZuulFilter to pass host name to next request but what I see is that original request to the Client is picked up by the ZuulFilter and I can add but not when the Feign request is sent to the backend service even if I map it in zuul (i guess that is intended ?).
I am not really sure what's the next step and would appreciate some suggestions.
Hope that it's of any use for you but we're doing sth similar in Spring-Cloud-Sleuth but we're using a ThreadLocal to pass span between different libraries and approaches (including Feign + Hystrix).
Here is an example with the highlighted line where we retrieve the Span from the thread local: https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java#L123

Aspect not getting called in Spring MVC

I have our aspect, annotation & MVC controller written as follows:
Aspect
#Aspect
public class AuditAspect {
#Around(value = "#annotation(com.test.Audit)")
public Object audit(ProceedingJoinPoint pjp) {
System.out.println("Inside the Audit aspect ...");
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable t) {
}
return result;
}
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Audit
{
AuditType auditType();
}
The controller:
#RestController
#RequestMapping("/patients")
public class PatientController {
#Audit(auditType = AuditType.PATIENT_LIST)
#RequestMapping(value="", method=RequestMethod.GET)
public APIResponse getPatients(HttpServletRequest request, HttpServletResponse response, #RequestParam(required = false, value="audit") String sAudit) {
System.out.println("Inside getPatients ...");
return null;
}
}
However, the aspect's audit method is not getting called whenever I make rest requests.
Looked around for some help. Found few posts where there were mentions of AspectJ not working with Spring MVC controllers. However, I tried this same example with a simple spring MVC application, and the aspect was getting called properly, even if controller methods were annotated. Not sure what is going wrong here. Any pointers/suggestions here would be very helpful.
The sample application I tried didn't have use of spring transaction manager, or integration with hibernate etc... Would that make any difference?
Also, given below is the context file entries:
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.test">
<context:include-filter type="aspectj" expression="com.test.AuditAspect" />
</context:component-scan>
<context:annotation-config />
In order to make Spring AOP work, both your aspect and the target object must be a Spring #Component.

Unit Testing I18N RESTful Web Services with Spring, RestTemplate and Java Config

Trying to get Unit Tests to work when using Spring RestTemplate and I18N. Everything in the setup works fine for all the other test cases.
Based upon what I read, this is what I put into the Java Config:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
return new LocaleChangeInterceptor();
}
#Bean
public DefaultAnnotationHandlerMapping handlerMapping() {
DefaultAnnotationHandlerMapping mapping = new DefaultAnnotationHandlerMapping();
Object[] interceptors = new Object[1];
interceptors[0] = new LocaleChangeInterceptor();
mapping.setInterceptors(interceptors);
return mapping;
}
#Bean
public AnnotationMethodHandlerAdapter handlerAdapter() {
return new AnnotationMethodHandlerAdapter();
}
Then in my usage with RestTemplate I have:
public MyEntity createMyEntity(MyEntity bean) {
Locale locale = LocaleContextHolder.getLocale();
String localeString = "";
if (locale != Locale.getDefault()) {
localeString = "?locale=" + locale.getLanguage();
}
HttpEntity<MyEntity> req = new HttpEntity<MyEntity>(bean);
ResponseEntity<MyEntity> response = restTemplate.exchange(restEndpoint + "/url_path" + localeString, HttpMethod.POST, req, MyEntity.class);
return response.getBody();
}
While this could be cleaned up a bit, it should work - but the LocalChangeInterceptor never gets invoked. I am debugging this now and will post again as soon as I figure it out - but in the hope this is a race condition that I lose - does anyone know why?
Was lucky and stumbled upon this thread. One of the notes clued me into the right direction. You don't need all those beans in the Java Config. But if you are using #EnableWebMvc as I am, but I didn't know it was important enough to even mention, all you need to do in your Java Config is:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
return new LocaleChangeInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
super.addInterceptors(registry);
}
Add the one bean for the Interceptor and then override the method to add the interceptor. Here my configuration class (annotated with #Configuration and #EnableWebMvc) also extends WebMvcConfigurerAdapter, which should be common usage.
This, at least, worked for me. Hope it may help someone else.

Resources