can #FeignClient extend - and #RestController implement - a common, fully-annotated Interface? - java

I want a Feign client to consume a Spring Boot controller, and I want the contract between them to be specified in a common Interface to the degree possible.
The interface with method would look something like this:
#RequestMapping
public interface RuleManager {
#RequestMapping(value = "/addRule", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody Rule addRule(#RequestBody Rule rule);
}
The Feign client would look like:
#FeignClient(url = "http://localhost:8080")
public interface RuleManagerClient extends RuleManager { }
and the Spring boot controller:
#RestController
public class RuleManagerService implements RuleManager {
#Override
#Transactional
public Rule addRule(#RequestBody Rule rule) {
return rule;
}
}
It's nice that I don't have to specify #RequestMapping in two places, but unfortunately it seems I do have to specify #RequestBody twice. When #RequestBody is omitted from either the controller or the shared interface, the Rule object is instantiated but with all members set to null.
Is there a way around this ? Perhaps this is addressed in a newer version ? My dependencies include:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.14.3</version>
</dependency>
I discovered this technique required at least feign-core 8.6 here:
https://jmnarloch.wordpress.com/2015/08/19/spring-cloud-designing-feign-client/
Thanks for any help.

Apparently this does work--#RequestBody need only appear in the shared Interface. The problem was that I had the following property set in application.properties for the controller but not for the client:
spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
That's why the object was instantiated on the server side but with all members null--effectively, the wrong properties were sent across the wire, for example "ruleName" instead of the expected "rule_name".

Related

How to update a flag in existing running Spring Boot Application without restart

I have a spring boot application running a feature. I want to toggle that feature(on/off) at runtime without redeploying or restarting the application. Issue is that I can't deploy any rest endpoint as server has only exposed some specific port because of security.
I want to remotely control the toggle so that I can set that feature on and off. I tried reading the environment variable on my local machine using:
System.getEnv("envVariable")
but even after updating it using export envVariable=true it's not reflecting updated value in the code.
Can someone suggest any way to achieve this ?
Thanks,
To do this you need some more dependencies.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
in properties file you need to write
management.endpoints.web.exposure.include=*
and on the class wherever you are are using environment variables use Annotation #RefreshScope like
import org.springframework.web.bind.annotation.RestController;
import org.springframework.cloud.context.config.annotation.RefreshScope;
#RefreshScope
#RestController
public class DemoController {
#Value("${my.data}")
String str;
// code
}
and whenever you are changing environment variable just hit a post request http://localhost:PORT/actuator/refresh
using above configuration you can change the environment variables.
There is a programming pattern Feature Toggle that provides a way to turn on/off application components during the runtime. The core idea is to ask property files or database config table about current states of config fields and change application functionality if config changed. This pattern described here https://martinfowler.com/bliki/FeatureToggle.html. You can find more by using keyword "Feature Flags".
One of popular implementations of Feature Flags for Java is togglz (https://www.togglz.org/quickstart.html).
Here is an exaple of using togglz:
Create enum for features representation
public enum MyFeatures implements Feature {
#EnabledByDefault
#Label("First Feature")
FEATURE_ONE,
#Label("Second Feature")
FEATURE_TWO;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
Implement TogglzConfig
#ApplicationScoped
public class DemoConfiguration implements TogglzConfig {
public Class<? extends Feature> getFeatureClass() {
return MyFeatures.class;
}
public StateRepository getStateRepository() {
return new FileBasedStateRepository(new File("/tmp/features.properties"));
}
public UserProvider getUserProvider() {
return new ServletUserProvider("admin");
}
}
Describe the feature behavior depended on toggle:
if( MyFeatures.FEATURE_ONE.isActive() ) {
// new stuff here
}
Source: https://www.togglz.org/quickstart.html

Spring boot validation annotations #Valid and #NotBlank not working

Given below is my main controller from which I am calling the getPDFDetails method.
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
System.out.println("contracts value is "+ updatePrintContracts);
Integer cancellationReasons = service.getPDFDetails(updatePrintContracts);
System.out.println("success!");
return ResponseEntity.ok(cancellationReasons);
}
Below is the UpdatePrintContracts class where I have defined all the variables with validation annotations and corresponding getter/setter methods.
public class UpdatePrintContracts {
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String isReprint;
#Valid
#NotBlank
Integer dealerId;
#Valid
#NotBlank
#Pattern(regexp = "\\p{Alnum}{1,30}")
String includeSignatureCoordinates;
#Valid
#NotBlank
java.util.List<Integer> contractNumbers;
public String getIsReprint() {
return isReprint;
}
public void setIsReprint(String isReprint) {
this.isReprint = isReprint;
}
public Integer getDealerId() {
return dealerId;
}
public void setDealerId(Integer dealerId) {
this.dealerId = dealerId;
}
public String getIncludeSignatureCoordinates() {
return includeSignatureCoordinates;
}
public void setIncludeSignatureCoordinates(String includeSignatureCoordinates) {
this.includeSignatureCoordinates = includeSignatureCoordinates;
}
public java.util.List<Integer> getContractNumbers() {
return contractNumbers;
}
public void setContractNumbers(java.util.List<Integer> contractNumbers) {
this.contractNumbers = contractNumbers;
}
}
I am trying to run the application as a Spring Boot app by right clicking on the project (Run As) and passing blank values for variables isReprint and includeSignatureCoordinates through Soap UI. However the validation doesn't seem to work and is not throwing any validation error in Soap UI. What am I missing? Any help is appreciated!
If you are facing this problem in latest version of spring boot (2.3.0) make sure to add the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Observation:
In earlier version of Spring Boot (1.4.7), javax.validation used to work out of the box. But, after upgrading to latest version, annotations broke. Adding the following dependency alone doesn't work:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
Because this provides JSR Specification but not the implementation. You can also use hibernate-validator instead of spring-boot-starter-validation.
For Anyone who is getting this issue with 2.0.1.Final:
In all SpringBoot versions above 2.2, Validations starter is not a part of web starter anymore
Check Notes here
So, all you have to do is add this dependency in your build.gradle/pom file
GRADLE:
implementation 'org.springframework.boot:spring-boot-starter-validation'
MAVEN
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
First you dont need to have #Valid annotation for those class variables in UpdatePrintContracts . You can delete them.
To trigger validation of a #Controller input, simply annotate the input argument as #Valid or #Validated:
#RequestMapping(value=PATH_PRINT_CONTRACTS, method=RequestMethod.POST)
public ResponseEntity<?> printContracts(#Valid #RequestBody final UpdatePrintContracts updatePrintContracts) throws Exception {
Refer here for full understanding of validating models in spring boot.
And If you want to check that a string contains only specific characters, you must add anchors (^ for beginning of the string, $ for end of the string) to be sure that your pattern matches all the string.Curly brackets are only to write a quantity,
#Pattern(regexp = "^[\\p{Alnum}]{1,32}$")
Lastly i assume you have following jars in your classpath,
.validation-api.jar (contains the abstract API and the annotation scanner)
.hibernate-validator.jar (contains the concrete implementation)
I faced the same error.
I had to use the below 2 dependencies alone:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And use #Validated annotation(import org.springframework.validation.annotation.Validated) on rest controller level and #Valid annotation at method argument level(import javax.validation.Valid)
If there are any other extra dependencies like javax.validation.validation-api, org.hibernate.hibernate-validator, etc then the validations stopped working for me. So make sure that you remove these dependencies from pom.xml
I was using This dependency of validation in spring boot and didn't work ,
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
I replaced it with spring-boot-starter-validation and it worked .
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-
starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.4.0</version>
this is for anyone here who still has the same issue after following the steps mentioned above. I had to restart my IDE (IntelliJ) for the changes to take effect.
My problem solved by this.
When we use classes inside classes that also need validations so #Valid needs to be annotated to all in that case.
Link for more details
Make sure to use #Valid annotation before #RequestBody
For newer versions of spring boot ensure all validation annotation are picked from jakarta.validation.* package and not javax.validation.*. As the annotations are named same in both.
Step-1: Add these two dependency in the pom.xml file
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Step-2: Create a Custom Exception class like this
package com.bjit.salon.auth.service.exceptions;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.stream.Collectors;
#ControllerAdvice
public class AnynameApplicationException {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<List<String>> processUnmergeException(final
MethodArgumentNotValidException ex) {
List<String> list = ex.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return new ResponseEntity<>(list, HttpStatus.BAD_REQUEST);
}
}
Step-3: Add #Valid annotation to the method arguments like this way
public ResponseEntity<?> registerAccount(#Valid #RequestBody UserRegisterDto
registerDto) {
// rest of the codes
}
You have to add this dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Note: SNAPSHOT, M1, M2, M3, and M4 releases typically WORK IN PROGRESS. The Spring team is still working on them, Recommend NOT using them.
You can use #NotEmpty will check for both blank and null values.
Add #Valid to your RestContoller class methods

Spring 3 - Annotation based Bean Validation

I am building a REST API. Its made up of a Resource ( #Controller ) which returns a response 204 even when one of the mandatory field is not present.
I am using Spring 3.1, validation-api (1.1.0.Final) & Hibernate-validator(4.3.0). Not sure if Hibernate-validator plays any role here.
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.0.Final</version>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
I have a spring controller #Controller and a Bean with #Component
#POST
#Consumes(value = {MediaType.APPLICATION_JSON})
#Produces(value = {MediaType.APPLICATION_JSON})
#RequestMapping(method = RequestMethod.POST)
public Response addUserData(#Valid #RequestBody UserData userDataRequest) throws ResourceException {
...
}
My UserData bean has
#Component
public class UserData {
#NotNull(message = "user ID should not be null")
#Min(value = 1, message = "user ID should not be empty")
#Max(value = 20, message = "user ID should have length of more than 20")
#Pattern(regexp="[A-Z][a-z]+", message = "Only Alphanumeric characters allowed")
private String userId;
}
My validations are not getting executed. When I dont pass "userId", there is no error thrown. What am I missing here ?
You must have the following about the infrastructure
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Autowired
private ValidatorConfig validatorConfig;
...
#Override
public Validator getValidator() {
return validatorConfig.localValidatorFactoryBean();
}
...
}
Where validatorConfig comes from
#Configuration
public class ValidatorConfig {
#Autowired
private ReloadableResourceBundleMessageSource rrbms;
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(){
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(rrbms);
return localValidatorFactoryBean;
}
}
And finally (I suggest you consider put the error messages in a .properties file, such as validation.properties how shown below)
#Configuration
public class MessageSourceConfig {
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasenames("classpath:/com/manuel/jordan/somethinga",
"classpath:/com/manuel/jordan/validation/validation");
resource.setDefaultEncoding("UTF-8");
return resource;
}
}
Some considerations or suggestions:
change #Valid to #Validated (see the API for the #Validated)
Remove the #Component for UserData (that represents a kind of entity right?). Remember that for that class each instance is unique and any bean managed by Spring is Singleton.
put the error messages in a .properties file
from where come the #POST, #Consumes and #Produces annotations?. They are not in the Spring API
Addition 01 about your comment:
Yes, you must use #EnableWebMVC. It indicates to Spring create some special beans internally for the web environment. See the #EnableWebMVC API. Is very important that annotation. Even for Rest I use that annotation.
About the Rest annotations, use the Spring annotations. Such as #RequestMapping and new 'variations' such as #GetMapping, #PostMapping etc.. That annotations contain the produces and consumes attributes. I have never seen your approach about mixing two sets of annotations from Rest.
Addition 02
The WebMvcConfigurerAdapter class represents the XML configuration file about all the Spring MVC infrastructure
Therefore for XML
#EnableWebMvc is equivalent <mvc:annotation-driven/>
About validation it should be: <mvc:annotation-driven validator="beanNameValidator" /> where the validator attribute according with the .xsd says:
Attribute : validator The bean name of the Validator that is to be
used to validate Controller model objects. This attribute is not
required, and only needs to be specified explicitly if a custom
Validator needs to be configured. If not specified, JSR-303
validation will be installed if a JSR-303 provider is present on the
classpath.
beanNameValidator according with my #Bean seems should be localValidatorFactoryBean
I ultimately ended up using Jersey Bean Validation, instead of Spring. This is because rest of my code is using Jersey anyways. To make this work I just imported Jersey Bean Validation jar and added a small change to web.xml. Validations are now working.
Thank you #Manual Jordan. I will upvote your answer, since it gave me the right clues.
<!-- jersey beanValidation -->
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>
Maybe passing to your "addUserData" method, a bindingResult Object, so you can test for and retrieve validation errors.
here is an example of how to use it : Validation form input

How to call a Spring managed object from a POJO?

I am running a web-app, which has one exposed class ( available to other POJO classes) and that has one autowired private member.
Spring managed class
public class EPSQueueSender {
#Autowired
private AmqpTemplate epsMessageTemplate;
public void dosomething(...){
epsMessageTemplate.convertAndSend(...); // Here epsMessageTemplate is null if instance of EPSQueueSender taken from other POJO
}
}
POJO class
public class Test{
EPSQueueSender sender = new EPSQueueSender();
sender.dosomething(....); // gives null exception on epsMessageTemplate
}
Spring code ( running as WebApp) and POJO class code( different Jar) are on same JVM. The POJO is not able to get initialized autowired object. However it is initialized if I use it in webApp project.
Can someone please give some suggestion how can I overcome this problem?
Last thing I would like to try is to hit webserver as http request from POJO.
beans can be pojo or xml many examples might help. You already have #autowired but you did not create the #bean method itself that belongs in a class annotated with #Configuration
Your problem could be overcome using #Configurable feature of spring. For it you have configure in xml with a code like belove
<context:annotation-config/>
<context:spring-configured/>
<context:load-time-weaver/>
in Java Congiguration like below:
#Configuration
#EnableAspectJAutoProxy
#EnableSpringConfigured
#EnableLoadTimeWeaving
public class ConfigApplicationContext {
}
with this configuration you can benefit of the load-waving aspect technique that througth the build-in Spring bean AnnotationBeanConfigureAspect you can inject Spring bean in a pojo that is annotated with #Configurable. you colud be have a code like below:
#Configurable
public class Test{
#Autowired
private EPSQueueSender sender;
public void method(){
sender.dosomething(....); // gives null exception on epsMessageTemplate
}
}
of course, since that you are using a load-wave technique you have configure an agent that will perform the istruments. the configuration is very simple and you have add a line like below in the start of the jvm or tomcat:
java -javaagent:path of the jar with the agent/spring-instrument.jar
remember of course of insert the aop and spring aop maven dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>yourVersion</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>4
<version>yourVersion</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>yourVersion</version>
</dependency>
I hope that this can help you

Swagger Spring API

I am using Spring Swagger library v1.0.2
Maven:
<dependency>
<groupId>com.mangofactory</groupId>
<artifactId>swagger-springmvc</artifactId>
<version>1.0.2</version>
</dependency>
I am able to scan my REST APIs and view it on the Swagger UI. I have even implemented OAuth and it is working great.
However, there is one feature that I need to implement. I want to hide some of the REST APIs. I need to do this at the class level as well as on the method level. I read about an 'hidden' attribute in the #Api annotation. I set it to 'true' but I can still see my class and all its method being displayed in the Swagger UI.
Example:
#Api(
description="This class is not covered by Spring security.",
value="/unauthorize",
hidden=true)
#RequestMapping("/unauthorize")
#Controller
public class UnauthorizeResource {}
Can someone please tell me how I can prevent the 'UnauthorizeResource' class from being displayed?
You can utilize the #ApiIgnore annotation:
#ApiIgnore
#RequestMapping("/unauthorize")
#Controller
public class UnauthorizeResource {}

Categories