Is that possible to run validation code before Populating Spring #Value - java

i have spring MVC controller
#Controller
#RequestMapping({ "/user/limits" })
public class UserController {
#Value("${wsgServiceURL}")
private String wsgServiceURL;
.
.
which populate wsgServiceURL value from property file
is that possible to run validation code on that value before population

Yes it is possible using type safe configuration properties through the #ConfigurationPropertiesmechanism
#Controller
#RequestMapping({ "/user/limits" })
#ConfigurationProperties("uc")
public class UserController {
// will map to uc.wsgServiceURL in property file
private String wsgServiceURL;
You can also add validation with #Validated and use use JSR-303 javax.validation

you can do something like the below,
#Controller
#RequestMapping({ "/user/limits" })
public class UserController {
private String wsgServiceURL;
#Autowired
public void initProperty(#Value("${wsgServiceURL}") String wsgServiceURL) {
if(wsgServiceURL== null) {
// Error handling here
}
}
}

Related

Spring: Implement two instances of a #Service with two instances of #Configuration linked to each respective service

An existing utility exists that I am importing to my project. It is written similarly as these two classes
#Service
public class ServiceAccessorImpl implements ServiceAccessor {
#Autowired private ServiceConfiguration serviceConfiguration;
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
#Configuration
#Data //automatically adds getters and setters
#ConfigurationProperties(prefix="config")//pulls serviceEndPoint value from external config
//Assume external config has config.serviceEndPoint = "www.endpoint1.com"
public class ServiceConfiguration {
private String serviceEndPoint;
}
In a separate project below I am importing the above into my project. I would like to have two instances of the same service with two unique and respective configuration classes. so that service1 is linked to config1 and service2 is linked to config2. My reasoning is I want one instance that only pulls the endpoint from the external configuration and another instance that I can use to set the endpoint. I have tried using things like #Qualifier but I cant figure out how to link the correct config with the correct service. I have a feeling that this may not be possible because ServiceConfiguration is privately scoped within ServiceAccessorImpl and I have no access through setters or constructors.
Controller Endpoint. The below is psuedo code of how I would like to implement my design. Autowiring in a single instance and using either endpoint works for that endpoint but not for both as shown below.
#ComponentScan(basePackageClass = ServiceAccessorImpl.class)
public class ServiceAccessorController {
#Autowired private ServiceAccessor serviceAccessor1;
#Autowired private ServiceConfiguration serviceConfiguration1;
#Autowired private ServiceAccessor serviceAccessor2;
#Autowired private ServiceConfiguration serviceConfiguration2;
Response CallEndpoint1(){
//www.endpoint1.com is already set here from external config
return serviceAccessor1.executeCall();
}
Response CallEndpoint1(){
serviceConfiguration2.setServiceEndPoint("www.endpoint2.com")
return serviceAccessor2.executeCall();
}
}
Thank you in advance
If you need multiple instances of the same implementation, it's easier to not annotate it as a bean, and have a #Configuration provide the beans instead.
public class ServiceAccessorImpl implements ServiceAccessor {
private ServiceConfiguration serviceConfiguration;
public ServiceAccessorImpl(ServiceConfiguration configuration) {
this.serviceConfiguration = configuration;
}
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
// this one should just have #ConfigurationProperties, not #Configuration
#Data
#ConfigurationProperties(prefix="config")
public class ServiceConfiguration {
private String serviceEndPoint;
}
then in your service you can have a Configuration providing both instances:
#Configuration
public class BeansConfiguration {
#Qualifier("service1")
#Primary // optional, Spring will autowire this instance by default if no qualifier is given
#Bean
public service1(#Autowired ServiceConfiguration config) {
// use the default config bean
return new ServiceAccessorImpl(config);
}
#Qualifier("service2")
#Bean
public service2() {
return new ServiceAccessorImpl(new ServiceConfiguration("www.endpoint2.com"));
}
}
then you may consume both by using the qualifiers (note that you don't have to inject the configs here):
public class ServiceAccessorController {
#Autowired
private ServiceAccessor serviceAccessor1;
#Autowired
#Qualifier("service2")
private ServiceAccessor serviceAccessor2;
Response CallEndpoint1(){
return serviceAccessor1.executeCall();
}
Response CallEndpoint2(){
return serviceAccessor2.executeCall();
}
}

Spring Boot application.properties custom variable in a non-controller class

How come application.properties will work in a RestController, but not in a service class?
//application.properties
test=test
Works Perfect!
#RestController
public class invitecontroller {
#Autowired inviteconfig inviteconfig;
#PostMapping("/v1/invite")
public void invite(#RequestBody XXX XXX) {
System.out.println(inviteconfig);
}
}
Returns "Null"
#Service
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
#Configuration
#Data
public class inviteconfig {
private String test;
}
The inviteservice class is not configured for Spring IoC (Inversion of Control) as a bean, so Spring will not handle the inviteservice class lifecycle. In this case, #Autowired is useless.
To fix this try to add #Component annotation to invitesevice, to declare it as a component:
#Component
public class inviteservice {
#Autowired inviteconfig inviteconfig;
public void invite() {
System.out.println(inviteconfig);
}
}
In the case of the controller, with #RestController, Spring will recognize your class as a Spring component.
Finally, don't forget to inject inviteservice using Spring IoC (using #Autowired annotation, or other means)
inviteservice class should be annotated with #Component or #Service
#Component
public class inviteservice {
...

How can I define RestController manually in Spring?

I've a lot of RestController class that annotated with #RestController and works correctly. but in a situation I have to add one of them manually. I think I can define a bean in Spring configuration class, so I can define a RestService, but how?
For example :
#Configuration
public class Config ..... {
............
#RestController
public MyRestService myRestService() {
if(shouldUseTypeA){
return new MyRestService<TypeA>(myParams);
}else{
return new MyRestService<TypeB>(myParams);
}
}
}
If shouldUseTypeA is something you know before the application starts, use it as a Spring profile and instantiate right controller according to the activated profile.
#Configuration
public class Config ..... {
// Type A
#Profile("shouldUseTypeA")
#RestController
public class TypeAService extends MyRestService<TypeA>(myParams){}
// Otherwise type B
#Profile("!shouldUseTypeA")
#RestController
public class TypeBService extends MyRestService<TypeB>(myParams){}
}

How to test if #Valid annotation is working?

I have the following unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {EqualblogApplication.class})
#WebAppConfiguration
#TestPropertySource("classpath:application-test.properties")
public class PostServiceTest {
// ...
#Test(expected = ConstraintViolationException.class)
public void testInvalidTitle() {
postService.save(new Post()); // no title
}
}
The code for save in PostService is:
public Post save(#Valid Post post) {
return postRepository.save(post);
}
The Post class is marked with #NotNull in most fields.
The problem is: no validation exception is thrown.
However, this happens only in testing. Using the application normally runs the validation and throws the exception.
Note: I would like to do it automatically (on save) and not by manually validating and then saving (since it's more realistic).
This solution works with Spring 5. It should work with Spring 4 as well. (I've tested it on Spring 5 and SpringBoot 2.0.0).
There are three things that have to be there:
in the test class, provide a bean for method validation (PostServiceTest in your example)
Like this:
#TestConfiguration
static class TestContextConfiguration {
#Bean
public MethodValidationPostProcessor bean() {
return new MethodValidationPostProcessor();
}
}
in the class that has #Valid annotations on method, you also need to annotate it with #Validated (org.springframework.validation.annotation.Validated) on the class level!
Like this:
#Validated
class PostService {
public Post save(#Valid Post post) {
return postRepository.save(post);
}
}
You have to have a Bean Validation 1.1 provider (such as Hibernate Validator 5.x) in the classpath. The actual provider will be autodetected by Spring and automatically adapted.
More details in MethodValidationPostProcessor documentation
Hope that helps
This is how I did it by loading ValidationAutoConfiguration.class into context:
#SpringBootTest
#ContextConfiguration(classes = { MyComponent.class, ValidationAutoConfiguration.class
public class MyComponentValidationTest {
#Autowired
private MyComponent myComponent;
#Test
void myValidationTest() {
String input = ...;
// static import from org.assertj.core.api.Assertions
assertThatThrownBy(() -> myComponent.myValidatedMethod(input))
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("my error message");
}
}
And MyComponent class:
#Component
#Validated
public class MyComponent {
public void myValidatedMethod(#Size(min = 1, max = 30) String input) {
// method body
}
)

Spring MVC: globally injecting properties from persistent layer

I have a service bean capable of getting / setting property values from persistent layer (eg: database). Something like this:
#Service
public ConfigService {
public String getConfig(String key);
}
The problem is for each controller class I write I have to autowire and populate my model with the property key/values:
#Controller
#RequestMapping("/foo")
public FooController {
#Autowired private ConfigService configService;
#RequestMapping("/login")
public String login(Model model) {
model.addAttribute("site.name", configService.getConfig("site.name"));
//...
}
}
Is there any way I can automatically get the value of this property on my spring JSP view? I don't want to have to inject this to my model object for each controller class I write.
The closest I can get so far is using Spring ResourceBundleMessageSource bean and <spring:message> tags, however I am constrained to using properties file, can't store it in database.
I found another way of doing this. Use a #ControllerAdvice class combined with #ModelAttribute method. Something like this:
#ControllerAdvice
public class ConfigAdvice {
#Autowired private ConfigService configService;
#ModelAttribute
public void populateConfig(Model model) {
for(Config config : configService.getAll()) {
model.addAttribute(config.getKey(), config.getValue());
}
}
}
The #ModelAttribute annotated populateConfig() method above will run prior to any #RequestMapping method on all other controller classes.
One drawback is config key can't contain any dot characters.
So far this looks to be my best option. Please let me know if there's a better way.

Categories