Property value could not be read when using #RefreshScope in spring controller - spring-boot

I am writing 2 applications using cloud config, one the server and another is the client. The server is running on 8888 and holding the configuration stored in GIT repository. It has the property:
spring.cloud.config.server.git.uri=${HOME}/Desktop/config-repo
The client is using the above configuration where it has one property as: message. Client is a restful webservice which has a controller as:
#RestController
class Controller{
#Value ("${message}")
private String message;
#Value ("${course}")
private String course;
#RequestMapping("/showMessage")
private String showMessage(){
return message;
}
#RequestMapping("/showCourse")
private String showCourse(){
return course;
}
}
When accessing http://localhost:8080/showCourse it is perfectly working and showing the message in the web page which is configured in the message.properties.
Issue when using #RefreshScope
Now when I am using #RefreshScope with the controller, I am not at all getting any output where the webpage is blank. When debugging, the issue is with
#Value ("${message}")
private String message;
#RequestMapping("/showMessage")
private String showMessage(){
return message;
}
in the controller where the message is null.
This happens when using the annotation - RefreshScope.
I have added the code/project at GIT: https://github.com/santoshkar/SpringBootCentralConfigUsingGit.git
Kindly help.
Thanks,
Santosh

Related

Spring Boot Mongo Repository Document DB Retryable writes are not supported

I'm currently trying to update a document using MongoRepository in Spring, where it is connecting to an AWS DocumentDB cluster. I am getting a 301 - Retryable writes are not supported error even though the URL used to connect to DocumentDB includes retryWrites=false, so I don't know how I'm supposed to update documents or if I'm supposed to disable retryWrites from somewhere else in Spring as well.
The URL for the DocumentDB connection looks like this:
mongodb://<username>:<password>#mongo-dev-cluster.cluster-xxxxx.eu-west-2.docdb.amazonaws.com:27017/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false
The code for the model, repository and service looks like this:
#Service
public class CarService {
#Autowired
private CarRepository carRepository;
public void update(String id, Car car) {
// Just saving wouldn't work because there is an indexed key
car.setId(id);
carRepository.save(car);
}
}
#Repository
public interface CarRepository extends
MongoRepository<Car, String> {
}
#Document
#TypeAlias("car")
public class Car {
#Id
private String id;
#Indexed(unique = true)
private String carName;
private String color;
}
The application.properties looks like this:
spring.data.mongodb.username=${DATABASE_USERNAME}
spring.data.mongodb.password=${DATABASE_PASSWORD}
spring.data.mongodb.database=cars-db
spring.data.mongodb.port=27017
spring.data.mongodb.host=mongo-dev-cluster.cluster-xxxxx.eu-west-2.docdb.amazonaws.com
How can I stop this error from happening when updating a document where I want to keep the ID and it's indexed values equal?
I figured out what I was doing wrong. I was configuring MongoDB in Spring to use host and port, meaning the retryWrites=false was never being read. Instead, I changed my application.properties to use a URI instead of a host and port. It now looks like this:
spring.data.mongodb.database=cars-db
spring.data.mongodb.uri=mongodb://<username>:<password>#mongo-dev-cluster.cluster-xxxxx.eu-west-2.docdb.amazonaws.com:27017/?retryWrites=false`
This means the retryWrites=false property is now being read correctly, whereas before, it was apparently only reading the host name so it wasn't disabling retryable writes.

Spring Boot rest service not accepting same request more than 8 request

From remote application I am trying to fetch one product eg. http://remotehost:8080/customers/C001/product/101 and getting the respective response on Postman. When tried to hit same request more than 8 times then till 8th request I am getting response, but when I tried 9th same request then not getting any response just showing "Loading", so I need to restart server every time.
my code as below
#RestController
#PreAuthorize("isAnonymous() or #customerId.equalsIgnoreCase(authentication.getPrincipal().getFirm())")
#RequestMapping(value = "/customers/{customerId}/product", produces = {"application/hal+json"})
public class ProductController {
#Resource(name = "productService")
ProductService productService;
#GetMapping({"/{productId}"} )
public HttpEntity<Product> getProduct(#PathVariable String productId, #PathVariable String customerId){
return new ResponseEntity<>(productService.getProduct(productId,customerId));
}
}
`
if I do code changes in code then its working properly eg. if change Pathvariable as ->
/{prodId} instead of /{productId} and /{custId} instead of /{customerId} as below
#RestController
#PreAuthorize("isAnonymous() or #customerId.equalsIgnoreCase(authentication.getPrincipal().getFirm())")
#RequestMapping(value = "/customers/{custId}/product", produces = {"application/hal+json"})
public class ProductController {
#Resource(name = "productService")
ProductService productService;
#GetMapping({"/{prodId}"} )
public HttpEntity<Product> getProduct(#PathVariable("prodId") String productId, #PathVariable("custId") String customerId){
return new ResponseEntity<>(productService.getProduct(productId,customerId));
}
}
then its allowing more than 8 request. why this is not working with productId and customerId PathVarialble?
In my project we have already used Redis. in our Redis lock intercepter we have already configured redis lock on PathVariable eg. /{productId} and /{customerId}. when i comment to add this interceptor in WebContext.xml then our service allowing multiple request instead on only 8 request and 8 is default in Redis property.
Now service is working.

How to get spring boot controller endpoint full url?

How to get a specific spring boot controller endpoint full url without concat/hardcode strings? My case is need send a request to external url and get async notification back from it, so I need pass my notification endpoint full url to it. Here is sample code:
#RestController
#RequestMapping("/api/v1")
public class MyController {
#PostMapping("/sendRequest")
public void sendRequest() {
...
// 1. get /notifyStatus endpoint full url to be send to external service
String myNotificationFullUrl = xxx ?
// 2. call external service
}
#GetMapping("/notifyStatus")
public void notifyStatus() {
...
}
}
Allows getting any URL on your system, not just a current one.
import org.springframework.hateoas.mvc.ControllerLinkBuilder
...
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(YourController.class).getSomeEntityMethod(parameterId, parameterTwoId))
URI methodUri = linkBuilder.Uri()
String methodUrl = methodUri.getPath()
There are more methods also to change a host name, etc.
if you don't want hardcode, maybe a possible solution is add a messages.properties with all your messages and urls. Then you can configure Spring MessageSource and get your url from that file.
Supose that you have a message.properties file with the following property:
url=full_url_to_your_service
In your #Controller, inject your configured MessageSource to allow Spring resolve the messages:
#Autowired
private MessageSource messageSource;
Then you can get your url as follow:
String url= messageSource.getMessage("url", put_here_your_locale);
If you need more information, check Spring doc for MessageSource.

Validating Mongo Documents in Spring Boot

I'm writing a rest service using spring boot with Jersey and MongoDB starter packages. So I have validation working on top level documents by creating the beans:
#Configuration
public class MongoValidationBeans {
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
I have a document:
#Document
public class SomeDocument {
#NotEmpty(message="error message that shows on console")
private Set<NonDocumentObject> referencesToOtherDocuments;
}
With set of embedded objects:
public class NonDocumentObject {
#NotNull(message="can't see this error message")
private ObjectId referenceId;
#NotBlank
private String referenceInfo;
}
The validation beans respect the #NotEmpty annotation on my set of objects, but they do not respect #NotNull or #NotBlank annotations on fields on my NonDocumentObject. How can I get validation to work on the fields of my embedded Set of objects.
EDIT: #Valid fixes the above problem.
Also, when a constraint violation happens on my top level document, I can see the specific message on my console but tomcat returns an http error page with response status 400. How can I instead send a json object with more information about the error? I have a class
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {}
which catches 404, 405, etc exceptions and returns a json object with the appropriate information, but does not catch mongo constraint validations. I think I need to throw exceptions from the mongo validation beans but can't find resources that direct me how to.
I also want to be able to embed other objects into NonDocumentObject with its own validation. Would it be possible?
So the #Valid annotation triggers cascade validation, but I still can't figure out how to catch validation errors with an exception mapper, or some other way to catch validation errors.

spring mvc 3 caching example

I have requirement for spring mvc 3 caching. Requirement is : while starting the server, we need to call database for one dropdown and put those values in the cache. So that whenever we required those values, we need to retrieve from cache.
Please help me with an example.
Thanks in advance.
May be you can use init-method (Spring 2.5) or #PostConstruct annotation (in Spring 3.0).
This method will be called during server start up
The following is code snippet
#Component
public class CacheDBData {
private String values[];
//add setter & getter
//This will be called during server start up after properties are initialised
#PostConstruct
public void getDataFromDB() {
values = //Logic to get data from DB and store that in values property
}
}
Suppose for example you can use in class as follows
#controller
public class HomeController {
#Autowired
private CacheDBData cacheDBData ;
//getter and setters
private void methodxyz() {
String values[] = cacheDBData.getValues();
}
}
I've had success with Ehcahe for Spring. There's a couple of config files to setup but after that you simply annotate the methods you want to cache the output from and it just works.
This has the advantage that you can change the values coming back from the service/database and NOT have to restart your app, unlike the accepted answer.

Resources