Spring boot 2.0 custom converter being ignored - spring

I've either completely miss-understood converters, or all the examples/blogs/docs I've found are assuming I've done a step... My understanding is that when I post a String, the converter will pick it up and give the Controller a POJO instead.
The problem I have is the converter is never being called.
The payload coming into the controller is a standard JSON API string
{
"data":{
"attributes":{
"created-date":null,
},
"type":"steaks"
}
}
The converter:
#Slf4j
#Configuration
public class SteakConverter implements Converter<String, Steak> {
#Override
public Steak convert(String value) {
log.info("converter value {}", value);
return new Steak.builder().build();
}
}
The controller:
#Slf4j
#CrossOrigin
#RestController
public class SteakController {
#PostMapping("/steaks")
public ResponseEntity<String> create(#RequestBody Steak steak) {
}
}
From reading all the blogs and docs I can find this should be all that's needed. However I've also tried manually registering with the following:
#Slf4j
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
log.info("Converter registered");
registry.addConverter(new SteakConverter());
}
}
This is called during application startup.
The only thing I can think of is that the converter/controller doesn't think the payload is in fact a String, so is ignoring it?
How can I debug this, or just make it work?
Here is a working sample application that shows my problem.
Cheers

Related

How can I make a Jmeter HTTP request with an Enum as Request Parameter?

I have a Spring Controller with this signature
public ResponseEntity<blabla> find(#RequestParam Long id, #RequestParam Long version, #RequestParam CheckedItemType type)
I am trying to make an http request with Jmeter setting the third parameter as:
type=0 text/plain and I get a 400 error code cause the controller can't cast a String to CheckedItemType.
Any idea about how I can solve this?
Here, is an example:
public enum Modes {
ALPHA, BETA;
}
String to enum Converter:
public class StringToEnumConverter implements Converter<String, Modes> {
#Override
public Modes convert(String from) {
return Modes.valueOf(from);
}
}
Aregister our Converter:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToEnumConverter());
}
}

Custom AbstractEndpoint listening to "/" (root)

I've implemented a starter that configures Swagger the way I like. In addition, I'd like to redirect every call to the app's root URL (e.g. localhost:8080) to /swagger-ui.html.
Therefore, I added an own AbstractEndpoint which is instantiated in the #Configuration class as follows:
#Configuration
#Profile("swagger")
#EnableSwagger2
public class SwaggerConfig {
...
#Bean
public RootEndpoint rootEndpoint() {
return new RootEndpoint();
}
#Bean
#ConditionalOnBean(RootEndpoint.class)
#ConditionalOnEnabledEndpoint("root")
public RootMvcEndpoint rootMvcEndpoint(RootEndpoint rootEndpoint) {
return new RootMvcEndpoint(rootEndpoint);
}
}
The respective classes look like this:
public class RootEndpoint extends AbstractEndpoint<String> {
public RootEndpoint() {
super("root");
}
#Override
public String invoke() {
return ""; // real calls shall be handled by RootMvcEndpoint
}
}
and
public class RootMvcEndpoint extends EndpointMvcAdapter {
public RootMvcEndpoint(RootEndpoint delegate) {
super(delegate);
}
#RequestMapping(method = {RequestMethod.GET}, produces = { "*/*" })
public void redirect(HttpServletResponse httpServletResponse) throws IOException {
httpServletResponse.sendRedirect("/swagger-ui.html");
}
}
As stated in public RootEndpoint(), the custom Endpoint is bound to /root. Unfortunately, I can't specify super(""); or super("/"); as those values throw an exception (Id must only contains letters, numbers and '_').
How can I achieve having a custom Endpoint listening to the root URL in a starter using #Configuration files to instantiate beans?
I solved it with an easier approach by adding a WebMvcConfigurerAdapter bean in the #Configuration:
#Bean
public WebMvcConfigurerAdapter redirectToSwagger() {
return new WebMvcConfigurerAdapter() {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("").setViewName("redirect:/swagger-ui.html");
}
};
}

Rest Custom HTTP Message Converter Spring Boot 1.2.3

I want to create a custom of HttpMessageConverter using Rest, Json, Spring Boot 1.2.3 and Spring 4, However my custom HTTPMessageConverter its' never called.
I have preformed the following steps :
1: Created a class that extends AbstractHttpMessageConverter
#Component
public class ProductConverter extends AbstractHttpMessageConverter<Employee> {
public ProductConverter() {
super(new MediaType("application", "json", Charset.forName("UTF-8")));
System.out.println("Created ");
}
#Override
protected boolean supports(Class<?> clazz) {
return false;
}
#Override
protected Employee readInternal(Class<? extends Employee> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
InputStream inputStream = inputMessage.getBody();
System.out.println("Test******");
return null;
}
#Override
protected void writeInternal(Employee t,
HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
// TODO Auto-generated method stu
}
}
2: I create a configuration class to register HTTPMessageConverters
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
System.out.println("Configure Message Converters");
converters.add(new ProductConverter());
super.configureMessageConverters(converters);
//super.extendMessageConverters(converters);
}
}
3: The rest class method
#RequestMapping(value="/{categoryId}" ,method=RequestMethod.POST, consumes="application/json")
#PreAuthorize("permitAll")
public ResponseEntity<ProductEntity> saveProduct(#RequestBody Employee employee , #PathVariable Long categoryId) {
logger.log(Level.INFO, "Category Id: {0}" , categoryId);
ResponseEntity<ProductEntity> responseEntity =
new ResponseEntity<ProductEntity>(HttpStatus.OK);
return responseEntity;
}
My Custom HTTPMessageCoverter it's created but is never called ? Is there a configuration or step I'm missing ? any input or advice is appreciated.
After overriding the (AbstractHttpMessageConverter) class methods, I found out there's two annotations for achieving polymorphism #JsonTypeInfo and #JsonSubTypes. For anyone who wants achieve polymorphism can use these two annotations.
I believe you want to configure these message converters using the configureMessageConverters method in a configuration class that extends WebMvcConfigurerAdapter. I've done this myself with a converter for CSV content. I've included that code below. This link shows an example as well. This link may also be helpful. It seems like with Spring configuration it is not always clear on the best place to configure things. :) Let me know if this helps.
#Configuration
public class ApplicationWebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(new CsvMessageConverter());
}
}
You will also need top modify your supports() method to return true for classes supported by the converter. See the Spring doc for AbstractHttpMessageConverter supports method.

#Specializes in Spring

CDI has the feature of Specialization, and I'm looking for that in the Spring world.
Details.
In CDI, the #Specializes annotation allows one to change the behaviour of a bean just by overriding it. This is completely transparent to users of that bean, e.g. if we'd have
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Specializes
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
we could
public class SomewhereElse {
#Inject
OneBean oneBean; // we know nothing of AnotherBean here!
public void guessWhosThere() {
return oneBean.whoAmI(); // yet it returns "AnotherBean"
}
}
This gets really useful as soon as OneBean is actually used with and without AnotherBean. For example, if OneBean is in one.jar and AnotherBean is in another.jar, we can change the bean's behaviour just by reconfiguring the classpath.
Question. Does something like Specialization also exist in Spring?
I could only find the #Primary annotation, which however has a different semantics: #Primary does not replace one bean, but only marks one of multiple alternatives as the primary one. Especially, as I understood, I could not build a deep inheritance hierarchy as it's possible with #Specializes.
Short answer
In Spring 4, this is not possible. Period. Still, in 2016, nothing like this is possible with Spring's obsolete dependency injection model.
Seems like there is no similar annotation in spring, but you can achive it via #Qualifier.
Beans:
#Resource("oneBean")
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Resource("anotherBean")
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
SomewhereElse:
public class SomewhereElse {
#Autowired
#Qualifier("anotherBean")
OneBean oneBean;
public void guessWhosThere() {
return oneBean.whoAmI(); // returns "AnotherBean"
}
}
Edited.
Also you can develop your own annotation and use it in BeanPostProcessor, look at spring docs here
OR even better to use CustomAutowireConfigurer, see here
With Spring boot, you could probably get a similar result by leveraging its auto-configure mechanism, e.g. with a bean condition such as #ConditionalOnMissingBean:
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Configuration
public class OneConfiguration {
#Bean
#ConditionalOnMissingBean
public OneBean getBean() { return new OneBean(); }
}
#Component
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
However, you would have to make sure that all configurations are built accordingly if you don't know for sure which ones will be specialized:
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
public class YetAnotherBean extends AnotherBean {
#Override
public String whoAmI() { return "YetAnotherBean"; }
}
#Configuration
public class OneConfiguration {
#Bean
#ConditionalOnMissingBean
public OneBean getBean() { return new OneBean(); }
}
#Configuration
public class AnotherConfiguration {
#Bean
#ConditionalOnMissingBean
public AnotherBean getBean() { return new AnotherBean(); }
}
#Configuration
public class YetAnotherConfiguration {
#Bean
#ConditionalOnMissingBean
public YetAnotherBean getBean() { return new YetAnotherBean(); }
}
// and so on...

Spring AOP - method aspect is not invoked

Sorry: Google Traductor (english basic)
Interface implemented by aspect:
public interface MinReader {
void interceptThoughts();
}
MinReader class that implements the interface, which contains the "Aspect"
#Aspect
public class Magician implements MinReader {
#Pointcut("within(paquetea.paqueteb.*)")
public void thinking() {
}
#Override
#Before("thinking()")
public void interceptThoughts() {
// Codigo
}
}
This is a part of my JavaConfig:
#Configuration
#EnableAspectJAutoProxy
public class SpringIdolConfig {
#Bean
public MinReader magician() {
return new Magician();
}
// Otros bean
}
the problem is that the "interceptThoughts" method is never invoked.
My "solution" is to change the return type of method "magician" in my JavaConfig:
"MinReader" -> "Magician"
#Bean
public Magician magician() {
return new Magician();
}
Why is that?
Is there any way to use "MinReader" instead of "Magician"?
UPDATE: these are the other beans:
package paquetea.paqueteb;
public interface Thinker {
void thinkOfSomething(String thoughts);
}
package paquetea.paqueteb;
public class Volunteer implements Thinker {
#Override
public void thinkOfSomething(String thoughts) {
//code
}
}
This is my full javaconfig:
#Configuration
#EnableAspectJAutoProxy
public class SpringIdolConfig {
#Bean
public MinReader magician() {
return new Magician();
}
#Bean
public Thinker volunteer() {
return new Volunteer();
}
}
I'm guessing a bit, but I think one of your problems is that you've got the pointcut expression wrong. If I've read the examples in here right, you should use:
#Pointcut("within(paquetea.paqueteb..*)")
Note the ..*, and not the .* that you had. I find that when I get pointcuts wrong, they can be very difficult to debug, as their normal way of failing is to just silently not attach to anything. (My preferred technique is to attach to methods annotated with a custom annotation; that makes it easy to control what's happening and easy to debug.)

Resources