Spring MVC Swagger issue - java

I am using Spring MVC + Swagger version 1.0.2 integration. I am facing issue of duplicates API being seen on the REST docs API. Not sure why?
But I debug the issue, I following link https://dzone.com/articles/documenting-your-spring-api and A 'simple' way to implement Swagger in a Spring MVC application, as per this link I added following code
#Configuration
#EnableSwagger
public class SwaggerConfig {
private SpringSwaggerConfig springSwaggerConfig;
#Autowired
public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
this.springSwaggerConfig = springSwaggerConfig;
}
#Bean
// Don't forget the #Bean annotation
public SwaggerSpringMvcPlugin customImplementation() {
return new SwaggerSpringMvcPlugin(this.springSwaggerConfig).apiInfo(
apiInfo()).includePatterns(".*");
}
private ApiInfo apiInfo() {
return new ApiInfo("API", "API",
null, "test#yahoo.com",
"License", "http://test.license");
}
}
But when I added, since then I see same API is getting loaded twice like below
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails) throws java.lang.Exception
Mapped "{[/api/student],methods=[POST],produces=[application/json]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.test.StudentController.getStudentDetails(com.test.model.StudentDetails)
throws java.lang.Exception
..................
How to prevent loading beans twice ?

I was able to solve this issue. The issue was nothing to do with your applicationContext.xml or mvc-rest.xml etc, your application code should not be using #EnableSpring at all, then it worked. Even this link https://github.com/springfox/springfox/issues/565#issuecomment-172956702 also suggests the same.

Related

Spring cloud loadbalancer - Feign + SimpleDiscoveryClient with healthcheck/retry

I'm struggling to switch from Ribbon to Spring Cloud Loadbalancer after upgrading spring cloud versions.
Setting up the SimpleDiscoveryClient with Feign was easy.
But the simplediscovery client is 'too simple'.
I want to add at least a healthcheck so it doesn't use an instance that is potentially down & preferably also a retry mechanism.
I've read the docs over & over but cannot find an easy way to set this up.
https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#instance-health-check-for-loadbalancer
I found this example custom config for the health check, but it doesn't work.
public class CustomLoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
if I run it as is, it throws the following missing bean error:
Method discoveryClientServiceInstanceListSupplier in XXX.CustomLoadBalancerConfig required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.
Can anyone give me some pointers on how to get this working or how I can replicate the ribbon behavior?
Add the below RestTemplate Bean in your configuration.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Try adding the following to your application class
#LoadBalancerClients(defaultConfiguration = LoadBalancerClientConfiguration.class)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
This will enable the creation of DiscoveryClientServiceInstanceListSupplier (note the class itself is not available in the context, so you can't just autowire it for testing).

How to use actuator endpoints programmatically?

I need some metrics in to the spring boot application. So I used MetricsEndpoint like below. But I can't access to metrics. invoke() method does not appear.
spring boot version: 2.2.6.RELEASE
#Controller
public class DashboardControllerImpl implements IDashboardController {
#Autowired
private MetricsEndpoint metricsEndpoint;
#Override
public ResponseEntity listDiskUsage() {
metricsEndpoint.invoke(); // invoke does not appear
// code block
}
}
I found some thing like that, that solved my problem!
((MetricsEndpoint.Sample)metricsEndpoint.metric("process.uptime", null).getMeasurements().get(0)).getValue()

Micronaut - What is Springframework #Bean equivalent?

I am very new to Micronauts and I have a fair bit of experience developing spring boot applications. With this background I was stumbled upon creating custom beans like how I used to create with #Bean annotations on Spring applications.
In my case I have a library that provides an Interface and its implementation class. I wanted to use the interface in my code and try to inject the implementation and it failes with below error
Caused by: io.micronaut.context.exceptions.NoSuchBeanException: No bean of type [io.vpv.saml.metadata.service.MetaDataParser] exists for the given qualifier: #Named('MetaDataParserImpl'). Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Here is my code
#Singleton
public class ParseMetadataImpl implements ParseMetadata {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Inject
#Named("MetaDataParserImpl")
private MetaDataParser metaDataParser;
#Override
public IDPMetaData getIDPMetaData(URL url) throws IOException {
logger.info("Parsing {}", url);
logger.info("metaDataParser {}", metaDataParser);
return metaDataParser.parseIDPMetaData(url);
}
}
I am sure there is somehting wrong I am doing and need to understand what to do. I have this working by adding below code and removing annotations around metaDataParser.
#PostConstruct
public void initialize() {
//Want to Avoid This stuff
this.metaDataParser = new MetaDataParserImpl();
}
Using Spring Boot it would be possible to add a #Bean annotation to create some custom beans we can do #Autowired to inject it everywhere on our application. Is there an equivalent on Micronauths that I am missing. I went through the guide on https://docs.micronaut.io/2.0.0.M3/guide/index.html and was not able to get anything to get this working.
Can someone suggest how I can use the #Inject to inject custom beans?
Just incase you want to see this, here is the application on Github.
https://github.com/reflexdemon/saml-metadata-viewer
With the help from Deadpool and a bit of reading I got what I was looking for. The solution was creating #BeanFactory
See Javadoc here: https://docs.micronaut.io/latest/guide/ioc.html#builtInScopes
The #Prototype annotation is a synonym for #Bean because the default scope is prototype.
Thus here is an example that will match the the behavior of Spring framework
Here is the answer for anyone who also is looking for such a thing.
import io.micronaut.context.annotation.Factory;
import io.vpv.saml.metadata.service.MetaDataParser;
import io.vpv.saml.metadata.service.MetaDataParserImpl;
import javax.inject.Singleton;
#Factory
public class BeanFactory {
#Singleton
public MetaDataParser getMetaDataParser() {
return new MetaDataParserImpl();
}
}

Spring Boot + Spring Security authorization success audit

Has anyone managed to get Spring Boot w/ Spring Security to handle AuthorizedEvent's (i.e. for audit log)?
I have implemented the following application event listener:
#Component
public class AuthorizationSuccessAudit implements ApplicationListener<AuthorizedEvent> {
private static Logger auditLogger = LoggerFactory.getLogger("audit");
#Override
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}
}
and have a test MVC endpoint annotated with #PreAuthorize. I was expecting that the spring security grants would show up on the log. While this works for every other event I used (AuthenticationSuccessEvent, AuthenticationFailureEvent, AbstractAuthenticationFailureEvent) it does not for the AuthorizedEvent.
I tried browsing the Spring Boot source and it seems this event is not handled in AuthorizationAuditListener.java, is this possibly a bug or am I hacking at it the wrong way?
As per spring boot documentation, Use Spring Boot Actuator (audit framework for Spring Boot), and provide your own implementations of AbstractAuthorizationAuditListener.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
And something similar to this..
class TestAuthorizationAuditListener extends AbstractAuthorizationAuditListener {
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
}
#Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
}
}
It looks like spring boot can not realize that here you want to handle event.
Try to annotate method so that spring knows that here you want to handle event
#EventListener(value = {AuthorizedEvent.class})
public void onApplicationEvent(AuthorizedEvent event) {
auditLogger.info("Authorization granted to user: {} - {}", event.getAuthentication().getName(), event.getConfigAttributes());
}
On successful authorization AuthorizedEvent should be triggered. make sure
FilterSecurityInterceptor should set setPublishAuthorizationSuccess true

Spring-boot - how to use both Resteasy JAX-RS and Spring MVC controllers

I have a jax-rs resources which I am trying to convert to spring boot. I have made them work but looks like some of jax-rs resources which are included in project as dependencies from other projects. I know it should be a service! Anyway so I may be able to make change to that library and make them mvc compatible but is there another way I can make spring servlet work with both? I am using all java based configuration btw no xml.
Main application class is currently annotated as following unless I add "/" in url-mapping which moots the point of adding this. I don't even see why I should add this if dispatcher servlet can find handler for every / request.
#SpringBootApplication(exclude = { EmbeddedServletContainerFactory.class })
#EnableAsync
#ImportResource(value = { "classpath:springmvc-resteasy.xml" })
public class Application implements AsyncConfigurer, DisposableBean { .. }
I think with above spring DispatcherServlet is still at front in control and it knows of rest easy handleradapters. By default boot also maps everything under '/' so I dont understand why it doesnt work. I am getting 404 when hitting jax-rs uri, spring mvc uri works.
I tried to add following but after that everything stopped working.
#Bean
public ServletRegistrationBean initServlet(DispatcherServlet dispatcherServlet) {
ServletRegistrationBean servletRegistrationBean
= new ServletRegistrationBean(dispatcherServlet, false,"/jaxrs-api/*", "/mvc-api/*");
servletRegistrationBean
.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return servletRegistrationBean;
}
I can see following beans are being loaded for resteasy upon context initialization.
resteasy.deployment
resteasy.registry
resteasy.dispatcher
resteasy.providerFactory
resteasy.spring.bean.processor
abstract.resteasy.handlerMapping
resteasy.handlerMapping
resteasy.handlerAdapter
resteasy.exception.handler
resteasy.no.resource.found.view
reateasy.error.view.resolver
It looks like things started working again after some try and errors which is not the way this should be! Spring-boot really should document here how it can work with spring mvc and jax-rs in co-existence.
Here's the sequence of changes I did.
I added #EnableWebMvc to my Application.java class or It can be on any other #Configuration class
#SpringBootApplication(exclude = { EmbeddedServletContainerFactory.class })
#EnableAsync
#EnableWebMvc
#ImportResource(value = { "classpath:springmvc-resteasy.xml" })
public class Application implements AsyncConfigurer, DisposableBean { .. }
In hindsight I knew this gonna create some problem and it did! My additional MessageConverter stopped working. I had following in Application.java
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper scalaObjectMapper = new ScalaObjectMapper();
//scalaObjectMapper.enable(features)
//objectMapper.registerModule(new DefaultScalaModule());
scalaObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(scalaObjectMapper);
return jsonConverter;
}
Now I had to move it to another config class which overrides WebMvcConfigurerAdapter to customize spring mvc beans.
#Component
#Configuration
public class WebMvcCustomConfig extends WebMvcConfigurerAdapter {
/**
* Registering Scala ObjectMapper
* #return
*/
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper scalaObjectMapper = new ScalaObjectMapper();
scalaObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(scalaObjectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//converters.add(ScalaObjectMapper);
super.configureMessageConverters(converters);
converters.add(customJackson2HttpMessageConverter());
}
}
So looks like now Spring MVC has some control rather then Spring Boot. I am sure this is gonna break loose when I have some additional components for example swagger2 integration or something similar.

Categories