#RefreshScope with #ConditionalOnProperty does not work - spring-boot

Problem
Exploring an option of enabling/disabling a #RequestMapping endpoint on demand without restarting JVM. Implement a kill switch on a endpoint at runtime.
Attempt
I've tried the following but #RefreshScope does not seem to work with #ConditionOnProperty
#RestController
#RefreshScope
#ConditionalOnProperty(name = "stackoverflow.endpoints", havingValue = "enabled")
public class MyController {
#Value("${stackoverflow.endpoints}")
String value;
#RequestMapping(path = "/sample", method = RequestMethod.GET)
public ResponseEntity<String> process() {
return ResponseEntity.ok(value);
}
}
Updated property using
POST /env?stackoverflow.endpoints=anyvalue
And reloaded context using
POST /refresh
At this point the controller returns the updated value.
Question
Are there any other ways to achieve the desired functionality?

Conditions in Boot are only applied at the Configuration class level or at the bean definition level.
This might help you.
public class MyWebConfiguration extends WebMvcConfigurationSupport {
#Bean
#ConditionalOnProperty(prefix="stackoverflow.endpoints",name = "enabled")
public MyController myController() {
return new MyController ();
}
}

As Spenser Gibb said here, this behavior is not supported.

Related

Spring Secured Rest Controller is not found in WebMVCTest

When using #Secured on a REST-Controller, implementing an interface, the Controller is not found in a #WebMvcTest. Either removing the #Secured annotation or removing the implements on the class will make it run in the test.
#Controller
#RequestMapping(path="/failing")
public class FailingTestController implements MyPasswordApi {
#RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE, path = "/test")
#Secured("ROLE_USER")
public ResponseEntity<GetEntity> getMethod()
and
#Controller
#RequestMapping(path = "/running")
public class RunningTestController {
#RequestMapping(method = GET, produces = MediaType.APPLICATION_JSON_VALUE, path = "/test")
#Secured("ROLE_USER")
public ResponseEntity<GetEntity> getMethod() {
are both used in different jUnit-5 Tests. The "RunningTest" will succeed (i.e. the GET-Request will have status 200), whereas the "FailingTest" will end up with a status 404. Using the injected RequestMapppingHanderMapping one can see, that the controller with the inheritance is not bound.
In fact, in the application, both controllers are found.
My question is, how to test an controller implementing security and an interface.
A testcase is found on github: https://github.com/sanddorn/Spring-Boot-Security-Rest-Showcase
The real answer could only be found in the github-repo (see question). The root problem was to annotate the application class with #SpringBootApplication and #EnableGlobalMethodSecurity like
#SpringBootApplication
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class TestApplication {
public static void main(String []argv) {
SpringApplication.run(TestApplication.class);
}
}
Using a different configuration class like
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class TestConfiguration {
}
and removing all extra annotation to the application-class solves the problem.
You are using wrong annotation, replace Controller to RestController
#RestController
#RequestMapping(path = "/running")
public class RunningTestController {
}

AutoConfigure RestController Spring Boot

I have tried to find documentation on how to manually configure a RestController (i.e in a Configuation class). That means without using the RestController annotation. Considering all the other annotations, like mapping, pathvariables etc. is at all possible?
A controller is essentially a component with a request mapping. See RequestMappingHandlerMapping.
#Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
If you want to instantiate a "rest controller" through configuration, you can probably do so via the following:
#Configuration
public class MyConfiguration {
#Bean
public MyController() {
return new MyController();
}
}
#ResponseBody
public class MyController {
#RequestMapping("/test")
public String someEndpoint() {
return "some payload";
}
}
But I don't think you'll be able to configure the request mappings (path variables, etc) in the configuration though; at least I haven't seen an example nor figured out how.

Mocking a #Bean defined in a #ConfigurationProperties Class

I have a #ConfigurationProperties class like this:
#ConfigurationProperties(prefix = "myprops", ignoreUnknownFields = false)
#Configuration
public class MyProperties {
private Long mySchedulerRate;
#Bean
public Long mySchedulerRate() {
return this.mySchedulerRate;
}
}
I'm registering it as a bean so I can refer to it in an annotation for a Spring scheduler:
#Scheduled(fixedRateString = "#{#mySchedulerRate}")
public void runScheduledUpdate() {
...
{
However, I now want to write a unit test where I want to be able to set a different value for the bean 'mySchedulerRate'. Mocking/Spying on the #ConfigurationProperties class doesnt seem to work since the scheduler gets set up before the stubbing has been set to return my desired value.
What is the easiest way to achieve what I am trying to do?
Managed to fix this now. I was running a #SpringBootTest and I realise you can override properties here within the annotation for a particular test class.
This worked for me:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class, properties = "myprops.my-scheduler-rate=1000")
public class MyTest {
So no need to try and override the bean, I was overcomplicating this far too much.

how i can define customized annotation on controller based on spring boot or cloud

I defined a series of annotation which could help me do some QPS measurement. I put them on controller method in spring cloud. Unfortunately, it does not work...
It does nothing if I put
#Metered(tags = {"test1", "test2"})
#Counted(tags = {"count1"})
on controller method.
Do I miss anything ?
#RestController
#Configuration
#ComponentScan
public class TestController {
  private Logger logger = LoggerFactory.getLogger(TestController.class);
#RequestMapping("exinfo")
#Metered(tags = {"test1", "test2"})
#Counted(tags = {"count1"})
public String getInfo(){
return "success";
}

Spring can not register spring hateoas resource assembler

I am using spring hateoas in spring and got the problem is spring could not instance hateoas resource assembler , here is my snippet code:
UserHateoasResourceAssembler.java:
#Service
public class UserHateoasResourceAssembler extends ResourceAssemblerSupport<UserDTO, UserHateoasResource> {
public UserHateoasResourceAssembler() {
super(UserController.class, UserHateoasResource.class);
}
#Override
public UserHateoasResource toResource(UserDTO entity) {
UserHateoasResource resource = createResourceWithId(entity.getId(), entity);
return resource;
}
#Override
protected UserHateoasResource instantiateResource(UserDTO entity) {
return new UserHateoasResource(entity);
}
}
UserController.java:
#RestController
#RequestMapping("/api/")
public class UserController {
#Inject
private UserHateoasResourceAssembler userAssembler ;
....
}
The exception was thrown is "No qualifying bean of type [UserHateoasResourceAssembler] found for dependency. I know this root cause is can not create instance of assembler.
I tried to use #Service or #Component but both does not work. I also tried to use #Autowire instead, but did not work too. I have to fix that by adding #Scope( proxyMode = ScopedProxyMode.TARGET_CLASS). But I wonder if there is any another solution to resolve it instead of using #Scope ?
Thanks.
I found the elegant solution. Due to my application using generated code and it used #EnableAspectJAutoProxy, this annotation default set auto-proxy = false and using JDK proxy, so almost the instance of class that implementing an interface was not allowed. We have to #inject the interface instead. So to inject the implementation class, have 2 options here:
Set #EnableAspectJAutoProxy(proxyTargetClass = true )
Remove this annotation if we does not really need that.

Resources