#refreshScope Spring cloud config doesn't refresh application.properties value - java

I tried to use the annotation #RefreshScope on my rest controller:
#RestController
#RefreshScope
public class SpringCloudControllerTest {
#Value("${data}")
private String value;
#GetMapping
public ResponseEntity<String> testPropertiesFile(){
return ResponseEntity.ok(value);
}
the #Value annotation refers the application.properties on my remote repository:
management.endpoints.web.exposure.include=*
data=2
if i change my file in this way:
management.endpoints.web.exposure.include=*
data=3
and run the request on my client side http://localhost:8081/actuator/refresh
the response is just:
[
"config.client.version"
]
I don't see any response about my changes and if i run the request
localhost:8081
the response is always "2"
these are my dependency on client side:
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-config', version: '2.2.6.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version:'2.3.7.RELEASE'
thank you all

Solved.
I change my application name file (client side) from application.yml to bootstrap.yml
Now when i run localhost:8081/actuator/refresh i have got this response
[
"config.client.version",
"data",
"spring.cloud.bootstrap.enabled"
]
thank you all

Related

Does spring boot actuator no longer have pause endpoint?

Spring Boot 2.6.6 - Actuator API Reference
I checked the above link and unable to find the /actuator/pause endpoint. I was not sure if it was my app that was causing the issue, so I created a new MVP from spring initializer and even then pause endpoint is not there.
I remember that there used to be a POST endpoint /actuator/pause, but how do I enable this on newer versions of Spring Boot(2.6.6 above)?
The below is my MVP's code.
build.gradle
plugins {
id 'org.springframework.boot' version '2.6.6'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
management:
endpoints.web.exposure.include: '*'
endpoint:
pause.enabled: true
restart.enabled: true
resume.enabled: true
shutdown.enabled: true
And once I start the app and hit
curl --location --request POST 'localhost:8080/actuator/pause', its sending 404.
You'll need to make sure that Spring Cloud Commons is included in your project dependencies, since it looks like that's the library that supplies the actuator/pause endpoint (https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#endpoints).
As the documentation additionally notes:
If you disable the /actuator/restart endpoint then the /actuator/pause and /actuator/resume endpoints will also be disabled since they are just a special case of /actuator/restart.
It looks like you should add the following dependency to your build.gradle file
implementation 'org.springframework.cloud:spring-cloud-starter:3.1.1
You can see that RestartEndpoint and
RestartEndpoint.PauseEndpoint classes actually are defined in this package.
Obviously you should change the application.yml like this:
management:
endpoints.web.exposure.include: '*'
endpoint:
pause:
enabled: true
restart:
enabled: true
It's easy to support it by yourself.
#RestController
#RequestMapping("/health")
public class HealthController implements HealthIndicator {
private Health health = Health.up().build();
#RequestMapping("/pause")
#ResponseBody
public String pause() {
health = Health.down().build();
return "success";
}
#RequestMapping("/start")
#ResponseBody
public String start() {
health = Health.up().build();
return "success";
}
#Override
public Health health() {
return health;
}
}

UnknownHostException for embedded Mongo DB tests in Jenkins build

I have a Spring Boot REST service and some unit tests written for the data layer. I use the embedded MongoDB dependency to perform the basic CRUD tests for my Repository class:
public interface UserRepository extends MongoRepository<UserEntity, String> {
Optional<UserEntity> findByUsername(String username);
}
I load the data from a JSON file (located under test/java/resources/data) and with the help of an ObjectMapper instance, I load the data into the embedded DB before each test and drop the collection after it's completed:
#DataMongoTest
class UserRepositoryTest {
// the path to the JSON file
private final File USER_DATA_JSON = Paths.get("src", "test", "resources", "data", "UserData.json").toFile();
// used to load a JSON file into a list of Users
private final ObjectMapper objectMapper = new ObjectMapper();
#Autowired
private MongoTemplate mongoTemplate; // makes the interaction with the embedded MongoDB much easier
#Autowired
private UserRepository userRepository;
#BeforeEach
void setUp() throws IOException {
// deserialize the JSON file to an array of users
UserEntity[] users = objectMapper.readValue(USER_DATA_JSON, UserEntity[].class);
// load each user into embedded MongoDB
Arrays.stream(users).forEach(mongoTemplate::save);
}
#AfterEach
void tearDown() {
// drop the users collection
mongoTemplate.dropCollection("users");
}
#Test
void testFindAllSuccess() {
// WHEN
List<UserEntity> users = userRepository.findAll();
// THEN
assertEquals(2, users.size(), "findAll() should return 2 users!");
}
// other test methods
}
In my local environment, everything works just fine, no configuration is needed inside the application.properties file. But when I execute a build on Jenkins, the following error appears for all the Repository tests:
UserRepositoryTest > testFindAllSuccess() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
Caused by: java.net.UnknownHostException at InetAddress.java:1642
Caused by: java.net.UnknownHostException at Inet6AddressImpl.java:-2
The build.gradle file dependencies are declared as follows:
implementation 'org.openapitools:openapi-generator-gradle-plugin:5.0.0'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation "io.springfox:springfox-boot-starter:3.0.0"
implementation('org.modelmapper:modelmapper:2.3.0')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
testImplementation 'io.projectreactor:reactor-test'
compile 'io.springfox:springfox-swagger-ui:3.0.0'
annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-configuration-processor'
compile 'org.mongodb:mongodb-driver-sync'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
// https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt
implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
I assume Spring Boot is not identifying the embedded MongoDB anymore. How can I configure it as simple as possible?
I saw there is a possibility of using containerized MongoDB and test it using #TestContainers, but for now I need to fix this issue since every build fails due to the tests.
LATER EDIT: By activating the --debug option in Jenkins when running the build, I discovered the following cause:
java.net.UnknownHostException: dev096.dev.cloud.******.eu: dev096.dev.cloud.******.eu: Name or service not known
Do you know if I have to add that address to the known hosts to my local machine (etc/hosts) or it should be configured locally with profiles (localhost for development and dev096.dev.cloud.******.eu for production)?
I managed to reproduce the same error locally and, if you haven't solved it yet, this is my take.
Remove all MongoDB custom configurations/properties, then add the following class to your project:
#Configuration
public class MongoTestConfig {
#Bean
MongoProperties properties() {
MongoProperties mongoProperties = new MongoProperties();
mongoProperties.setPort(33333);
mongoProperties.setHost("localhost");
return mongoProperties;
}
}
Make sure you tag your test class (UserRepositoryTest) with these three annotations:
#RunWith(SpringRunner.class)
#DataMongoTest
#Import(MongoTestConfig.class)
#DataMongoTest ignores all other bean definitions so we force the test to include the configuration that we just created using the explicit #Import.
Now the test will be forced to run on localhost. Hope it's going to work for you as it did for me! 😁
If dev096.dev.cloud.******.eu is a host used only in production, then your production server needs to know that host; not your local PC, nor Jenkins.
Ideally, you'd run your Jenkins tests using a 'jenkins' Spring profile, and then define the localhost in application-jenkins.properties.
Unfortunately, none of your solutions worked for me, but I found out later that dev096.dev.cloud.******.eu is the Jenkins instance itself.
The fix was to add an entry in /etc/hosts/ such that the server's IP to be also recognized as dev096.dev.cloud.******.eu

How to connect a grails app to Consul through micronaut Http Client?

I have a grails 3.2.0 web profile app. I want to connect to a separate micronaut app which is running fine and is connected properly to consul locally.
Here is my micronaut controller :
#Controller("/test")
class TestController {
#Get("/")
def index(){
return "Welcome to micronaut"
}
#Get("/test")
Single<String> test() {
return Single.just("Hello There")
}
}
Here is my grails controller :
class CardClientController {
#Autowired
NewCardClient newCardClient
def index() {
def result = newCardClient.getCards()
respond result
}
}
NewCardClient interface :
#Client("firstmicroservice")
public interface NewCardClient {
#Get("/test/test")
Single<String> getCards();
}
dependencies i've added in grails app :
compile 'io.micronaut:inject-java:1.0.0.M1'
compile 'io.micronaut:discovery-client:1.0.0.M1'
compile 'io.micronaut:spring:1.0.0.M1'
runtime 'io.micronaut:runtime:1.0.0.M1'
When i hit the grails controller as /cardClient/, i am getting following error:
2019-05-03 18:20:30.333 ERROR --- [nio-8200-exec-1] i.m.retry.intercept.RecoveryInterceptor : Type [grailsmono.NewCardClient$Intercepted] executed with error: Cannot convert publisher into target type: class io.reactivex.Flowable
java.lang.UnsupportedOperationException: Cannot convert publisher into target type: class io.reactivex.Flowable
at io.micronaut.cache.interceptor.CacheInterceptor.interceptPublisher(CacheInterceptor.java:352)
at io.micronaut.cache.interceptor.CacheInterceptor.intercept(CacheInterceptor.java:120)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at io.micronaut.discovery.client.$CachingCompositeDiscoveryClientDefinition$Intercepted.getInstances(Unknown Source)
at io.micronaut.discovery.client.CachingCompositeDiscoveryClient.getInstances(CachingCompositeDiscoveryClient.java:38)
at io.micronaut.http.client.loadbalance.DiscoveryClientRoundRobinLoadBalancer.select(DiscoveryClientRoundRobinLoadBalancer.java:58)
at io.micronaut.http.client.DefaultHttpClient.resolveRequestURI(DefaultHttpClient.java:720)
at io.micronaut.http.client.DefaultHttpClient.exchange(DefaultHttpClient.java:410)
at io.micronaut.http.client.DefaultHttpClient.exchange(DefaultHttpClient.java:154)
at io.micronaut.http.client.HttpClient.retrieve(HttpClient.java:125)
at io.micronaut.http.client.RxHttpClient.retrieve(RxHttpClient.java:60)
at io.micronaut.http.client.RxHttpClient.retrieve(RxHttpClient.java:33)
at io.micronaut.http.client.interceptor.HttpClientIntroductionAdvice.intercept(HttpClientIntroductionAdvice.java:331)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at io.micronaut.retry.intercept.RecoveryInterceptor.intercept(RecoveryInterceptor.java:74)
at io.micronaut.aop.MethodInterceptor.intercept(MethodInterceptor.java:41)
at io.micronaut.aop.chain.InterceptorChain.proceed(InterceptorChain.java:155)
at grailsmono.NewCardClient$Intercepted.getCards(Unknown Source)
...
Couldn't connect to consul and not able to debug the cause. Please help!!!
With the help of #JeffScottBrown, my workaround got simple just by changing the dependencies version of each as follows :
Added mavenCentral() repository as dependency :
repositories {
mavenLocal()
mavenCentral()
maven { url "https://repo.grails.org/grails/core" }
}
and the required micronaut dependencies as follows :
compile 'io.micronaut:micronaut-inject-java:1.1.0'
compile 'io.micronaut:micronaut-discovery-client:1.1.0'
compile 'io.micronaut:micronaut-spring:1.1.0'
runtime 'io.micronaut:micronaut-runtime:1.1.0'

Spring boot application, swagger-ui ApiInfo() method deprecated. Need Alternative

I have a Spring Boot application which use swagger-ui to expose it's apis.
Now, till I was using springfox-swagger-ui version 2.6.1 my code was working properly. But when I updated the version to 2.7.0 it throws an error that ApiInfo method is deprecated. Can anyone tell me an alternative which will modify the existing code as less as possible and successfully run the application with the Info still there in the Swagger UI as description. I'm giving the existing code of swagger config here...
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.xyz.abc"))
.paths(regex("/api.*"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfo(
"My-Project Api",
"Api for My Project",
"V1",
"NA terms of service url",
new Contact("Team Name", "www.somexyzteamcontact.com, "NA"),
"A license given",
"NA");
}
}
And gradle dependency for swagger which I used in My Project:
compile group: 'io.springfox', name: 'springfox-swagger2', version: '2.7.0'
compile group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.7.0'
Try the following, using ApiInfoBuilder to build API
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("API Reference").version("1.0.0")
.description("something")
.build();
}

#Transactional has no effect in my Spring Boot Application

This is a pretty confusing one. I've read dozens of links that purport to explain how to use #Transactional but I've verified no transaction is being created.
Main.java
#SpringBootApplication
#EnableJpaRepositories(basePackages="com.mypackage")
#EnableTransactionManagement
#EntityScan(basePackages=["com.mypackage"])
#EnableJpaAuditing
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
SubscriptionController.java
#RequestMapping("/api/subscription")
#RestController
public class SubscriptionController {
#Autowired SubscriptionService subscriptionService;
Logger log = LogManager.getLogger(this.getClass().getName());
public Collection<Subscriptions> subscribe(...) {
log.info("transName: " + TransactionSynchronizationManager.getCurrentTransactionName + ", isAlive: " + TransactionSynchronizationManager.isActualTransactionActive());
return subscriptionService.getAllSubscriptions();
}
}
SubscriptionService.java
#Service
public class SubscriptionService {
#Transactional public Collection<Subscription> getAllSubscriptions() {
log.info("transName: " + TransactionSynchronizationManager.getCurrentTransactionName() + ", isAlive: " + TransactionSynchronizationManager.isActualTransactionActive());
//return subscriptions via JPQL queries here
}
}
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RELEASE")
}
}
plugins {
id 'war'
}
apply plugin: 'org.springframework.boot'
repositories {
mavenCentral()
}
def springVersion = '5.0.3.RELEASE'
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.11'
testCompile group: 'junit', name: 'junit', version: '4.12'
compile "org.springframework.security:spring-security-core:${springVersion}", exclude
compile "org.springframework.security:spring-security-config:${springVersion}", exclude
compile "org.springframework.security:spring-security-web:${springVersion}", exclude
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "org.springframework:spring-test:${springVersion}", exclude
implementation 'org.springframework.boot:spring-boot-starter-web:2.0.5.RELEASE', exclude
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.5.RELEASE'
compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.11'
compile 'org.liquibase:liquibase-core:3.6.1'
compile 'org.liquibase:liquibase-groovy-dsl:1.2.2'
}
So when I run the api call, I get null, false as the log output. The contract for #Transactional says that the transactional aspect code will be weaved into the annotated transactional method, such that there will be a transaction (and thus an entitymanager and db connection) set up before the method and closed some time afterward. But that is irrelevant becuase shouldn't spring be creating an entitymanager before the controller is run? Both things aren't working here. Neither spring, nor #Transactional, is setting up any transaction. This results in failure to do any kind of query except for what's doable via a subclasses of JpaRepository. Somehow my Jpa repositories are able to set up transactions for their own methods. But what if their results have lazily initialized properties? I need a hibernate session to get those. So I need a transaction.
Try to remove
#EnableJpaRepositories(basePackages="com.mypackage")
#EnableTransactionManagement
SpringBoot should do those things automatically
What class are you using for #Transactional?
You should use org.springframework.transaction.annotation.Transactional.
An out-of-the-box Spring-boot application using spring initializer (https://start.spring.io/) will handle transactions properly. If you make yourself a sandbox with a unit test you can observer that the transaction demarcation done in your example will produce a transaction. Here is my git hub example: https://github.com/skjenco/hibernateSandbox.git
Code:
#Test
#Transactional
public void test() {
logger.info(TransactionSynchronizationManager.getCurrentTransactionName());
.....
For transaction demarcation to work you must be in an object managed by a Spring (Component, Service, Managed Beans, etc). In the case above it appears that you are in a Spring managed service. Using a working sandbox may be helpful in trouble shooting your issue--that is what I have done to solve perplexing hibernate issues.
You may also want to post your pom.xml file. spring-data or spring-tx dependency should be added for auto configurer to create transaction manager. Otherwise explicitly create a transaction manager bean and run the code again.
You can enable TRACE level logs for org.springframework and see if transaction manager is initialized.
Also check transaction interceptor logs. Use logging.level.org.springframework.transaction.interceptor=TRACE

Categories