How #Counted works in spring boot? - spring-boot

How #Counted works?
I have added #Counted annotation on my method in Controller and expecting to see how many hits are coming to the controller. But i cannot see metrics added onto the url http://localhost:8080/actuator/prometheus.
#Counted(value = "counted.success.test",description = "testCounter")

You need to add a CountedAspect as a bean, then the metrics are created when you call the method:
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class Config {
#Bean
CountedAspect countedAspect(MeterRegistry registry) {
return new CountedAspect(registry);
}
(Can't remember why we added the #EnableAspectJAutoProxy(proxyTargetClass = true))
Though that that kind of instumentation is not prefect, the labels class and method will change as soon as you refactor your code and your Grafana dashboard might not work any longer.

Related

Spring Boot - Is it possible to disable an end-point

Assuming I have a controller like:
public class MyController {
public String endpoint1() {...}
public String endpoint2() {...}
}
I want to disable endpoint1 for whatever reason in Spring. Simply, just disable it so that it cannot be accessed. So, I am not looking for how and what response to return in that case or how to secure that endpoint. Just looking to simply disable the endpoint, something like #Disabled annotation on it or so.
SOLUTION UPDATE:
Thanks all who contributed. I decided to go with #AdolinK suggestion . However, that solution will only disable access to the controller resulting into 404 Not Found. However, if you use OpenApi, your controller and all of its models such as request/response body will still show in swagger.
So, in addition to Adolin's suggestion and also added #Hidden OpenApi annotation to my controllers like:
In application.properties, set:
cars.controller.enabled=false
Then in your controller, use it. To hide controller from the OpenApi/Swagger as well, you can use #Hiden tag:
#Hidden
#ConditionalOnExpression("${cars.controller.enabled}")
#RestController
#RequestMapping("/cars")
public class Carontroller {
...
}
After this, every end point handled by this controller will return 404 Not Found and OpenApi/Swagger will not show the controllers nor any of its related schema objects such as CarRequestModel, CarResponseModel etc.
You can use #ConditionalOnExpression annotation.
public class MyController {
#ConditionalOnExpression("${my.controller.enabled:false}")
public String endpoint1() {...}
public String endpoint2() {...}
}
In application.properties, you indicates that controller is enabled by default
my.controller.enabled=true
ConditionalOnExpression sets false your property, and doesn't allow access to end-point
Why not remove the mapping annotation over that method?
Try this simple approach: You can define a property is.enable.enpoint1 to turn on/off your endpoint in a flexible way.
If you turn off the endpoint, then return a 404 or error page, which depends on your situation.
#Value("${is.enable.enpoint1}")
private String isEnableEnpoint1;
public String endpoint1() {
if (!"true".equals(isEnableEnpoint1)) {
return "404";
}
// code
}

Custom nested actuator endpoint

Using Spring Boot 1.0, I was able to customize the actuator endpoints as follows...
endpoints.beans.id=foo/springbeans
This would expose the spring beans endpoint at /foo/springbeans. However, in the latest Spring Boot this is not possible due to the following code in the AbstractEndpoint...
#NotNull
#Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'")
private String id;
I tried using the underscore, but that just exposes the endpoint at /foo_springbeans. This lead me to try to add a view controller so I could at least redirect or forward to the default endpoint, but I couldn't find an easy way to do that either. How can I configure the endpoint or a redirect?
After opening an issue with Spring Boot and being told to simply move the entire management context as suggested by Rafal, I was able to achieve what I was looking for, albeit with more code than I'd like. I created a custom MvcEndpoint as follows...
#Named
public class MyCustomHealthCheck extends EndpointMvcAdapter {
private HealthEndpoint delegate;
#Inject
public MyCustomHealthCheck(HealthEndpoint delegate) {
super(delegate);
this.delegate = delegate;
}
#ResponseBody
#RequestMapping(value = "/springbeans", method = GET)
public Health foo() {
return delegate.invoke();
}
}
The code above creates the /springbeans path underwhatever path the HealthEndpoint is mapped to, which is fine enough for my usecase. If I wanted it mapped to an entirely separate path, I would have needed to create a dummy endpoint and stick this MvcEndpoint under that.
For Spring 1.x Following property should help you:
endpoints.beans.path: /foo/springbeans
You can use it with any standard endpoint and if you want to use it with custom endpoint that extends AbstractEndpoint then you need additional annotation:
#ConfigurationProperties(prefix = "endpoints.customEndpoint")
and then use property:
endpoints.customEndpoint.path: /custom/endpoint

#transactional in spring jpa not updating table

I am using spring jpa transactions in my project.One Case includes inserting a data in a synchronized method and when another thread accesses it the data is not updated.My code is given below :
public UpdatedDTO parentMethod(){
private UpdatedDTO updatedDTO = getSomeMethod();
childmethod1(inputVal);
return updatedDTO;
}
#Transactional
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
Now if two threads access at the same time and if first thread completes childmethod2 and childmethod1 and without completing parentMethod() after that if second thread comes to the childMethod1() and checks if data exists,the data is null and is not updated by first thread.I have tried many ways like
#Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized childmethod1(inputVal){
//SomeCodes
//Place where update takes place
TableEntityObject obj = objectRepository.findByInputVal(inputVal);
if(obj == null){
childMethod2(inputVal);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void childMethod2(inputVal){
//Code for inserting
TableEntityObject obj = new TableEntityObject();
obj.setName("SomeValue");
obj.setValueSet(inputVal);
objectRepository.save(obj);
}
also tried taking off #transactional in the childMethod1() but nothing works out.I know im doing something wrong here , but couldnt figure out where and what exactly i am doing wrong.Can anyone help me out with this
#Transactional is resolved using proxies on spring beans. It means it will have no effect if your method with #Transactional is called from the same class. Take a look at Spring #Transaction method call by the method within the same class, does not work?
The easiest would be moving those methods into separate service.
Typical checklist I follow in cases like these :
If Java based configuration then make sure
#EnableTransactionManagement annocation is present in the class
containing the #Configuration annotation
Make sure the transactionManager bean is created, again this should be mentioned in the configuration class.
Use of #Transactional annocatio over the method which is calling the repository, typically a class in the DAO layer
Adding the #Service annotation for the class which is invoking the methods in the repository
Nice blog which explains the Transaction configuration with JPA in depth --> http://www.baeldung.com/2011/12/26/transaction-configuration-with-jpa-and-spring-3-1/68954

Spring Boot equivalent to XML multi-database configuration

I would like to port two projects to Spring Boot 1.1.6. The are each part of a larger project. They both need to make SQL connections to 1 of 7 production databases per web request based region. One of them persists configuration setting to a Mongo database. They are both functional at the moment but the SQL configuration is XML based and the Mongo is application.properties based. I'd like to move to either xml or annotation before release to simplify maintenance.
This is my first try at this forum, I may need some guidance in that arena as well. I put the multi-database tag on there. Most of those deal with two connections open at a time. Only one here and only the URL changes. Schema and the rest are the same.
In XML Fashion ...
#Controller
public class CommonController {
private CommonService CommonService_i;
#RequestMapping(value = "/rest/Practice/{enterprise_id}", method = RequestMethod.GET)
public #ResponseBody List<Map<String, Object>> getPracticeList(#PathVariable("enterprise_id") String enterprise_id){
CommonService_i = new CommonService(enterprise_id);
return CommonService_i.getPracticeList();
}
#Service
public class CommonService {
private ApplicationContext ctx = null;
private JdbcTemplate template = null;
private DataSource datasource = null;
private SimpleJdbcCall jdbcCall = null;
public CommonService(String enterprise_id) {
ctx = new ClassPathXmlApplicationContext("database-beans.xml");
datasource = ctx.getBean(enterprise_id, DataSource.class);
template = new JdbcTemplate(datasource);
}
Each time a request is made, a new instance of the required service is created with the appropriate database connection.
In the spring boot world, I've come across one article that extended TomcatDataSourceConfiguration.
http://xantorohara.blogspot.com/2013/11/spring-boot-jdbc-with-multiple.html That at least allowed me to create a java configuration class however, I cannot come up with a way to change the prefix for the ConfigurationProperties per request like I am doing with the XML above. I can set up multiple configuration classes but the #Qualifier("00002") in the DAO has to be a static value. //The value for annotation attribute Qualifier.value must be a constant expression
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
On the Mongo side, I am able to define variables in the configurationProperties class and, if there is a matching entry in the appropriate application.properties file, it overwrites it with the value in the file. If not, it uses the value in the code. That does not work for the JDBC side. If you define a variable in your config classes, that value is what is used. (yeah.. I know it says mondoUrl)
#ConfigurationProperties(prefix = "spring.mongo")
public class MongoConnectionProperties {
private String mondoURL = "localhost";
public String getMondoURL() {
return mondoURL;
}
public void setMondoURL(String mondoURL) {
this.mondoURL = mondoURL;
}
There was a question anwsered today that got me a little closer. Spring Boot application.properties value not populating The answer showed me how to at least get #Value to function. With that, I can set up a dbConfigProperties class that grabs the #Value. The only issue is that the value grabbed by #Value is only available in when the program first starts. I'm not certain how to use that other than seeing it in the console log when the program starts. What I do know now is that, at some point, in the #Autowired of the dbConfigProperties class, it does return the appropriate value. By the time I want to use it though, it is returning ${spring.datasource.url} instead of the value.
Ok... someone please tell me that #Value is not my only choice. I put the following code in my controller. I'm able to reliably retrieve one value, Yay. I suppose I could hard code each possible property name from my properties file in an argument for this function and populate a class. I'm clearly doing something wrong.
private String url;
//private String propname = "${spring.datasource.url}"; //can't use this
#Value("${spring.datasource.url}")
public void setUrl( String val) {
this.url = val;
System.out.println("==== value ==== " + url);
}
This was awesome... finally some progress. I believe I am giving up on changing ConfigurationProperties and using #Value for that matter. With this guy's answer, I can access the beans created at startup. Y'all were probably wondering why I didn't in the first place... still learning. I'm bumping him up. That saved my bacon. https://stackoverflow.com/a/24595685/4028704
The plan now is to create a JdbcTemplate producing bean for each of the regions like this:
#Configuration
#ConfigurationProperties(prefix = "Region1")
public class DbConfigR1 extends TomcatDataSourceConfiguration {
#Bean(name = "dsRegion1")
public DataSource dataSource() {
return super.dataSource();
}
#Bean(name = "00001")
public JdbcTemplate jdbcTemplate(DataSource dsRegion1) {
return new JdbcTemplate(dsRegion1);
}
}
When I call my service, I'll use something like this:
public AccessBeans(ServletRequest request, String enterprise_id) {
ctx = RequestContextUtils.getWebApplicationContext(request);
template = ctx.getBean(enterprise_id, JdbcTemplate.class);
}
Still open to better ways or insight into foreseeable issues, etc but this way seems to be about equivalent to my current XML based ways. Thoughts?

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