I'm using Spring Boot version 2.2.5.RELEASE and have trouble to receive any information from the actuator endpoint. What I'm receiving is only the empty structure of Actuator as following:
This is my configuration:
implementation "org.springframework.boot:spring-boot-starter-actuator"
#Bean
public HttpTraceRepository httpTraceRepository() {
return new InMemoryHttpTraceRepository();
}
management:
endpoints:
web:
exposure:
include: "*"
What do I miss?
Hallelujah, finally found the issue of my above described problem!
The problem was that i configured my spring boot application to use Gson instead of the default Jackson json parser. Not completely clear why, but with the following lines of code it results in issues to show the data at the actuator endpoints.
implementation ("org.springframework.boot:spring-boot-starter-web") {
exclude group: "org.springframework.boot", module: "spring-boot-starter-json"
}
implementation "com.google.code.gson:gson:2.8.6"
The following above described Bean is also unnecessary!
#Bean
public HttpTraceRepository httpTraceRepository() {
return new InMemoryHttpTraceRepository();
}
The following configuration was hence as well a problem:
spring:
http:
converters:
preferred-json-mapper: gson
gson:
exclude-fields-without-expose-annotation: true
And finally with removing of all of my #Expose annotations it started to work perfectly fine again.
#Expose
private String content;
If anyone can explain to me why this happened, or how to configure Gson to work properly with Spring Boot Actuator in combination, I'm here to listen and understand.
I had the same problem and I fixed by removing the following line
gsonBuilder.excludeFieldsWithoutExposeAnnotation()
Related
I have a spring boot project with cloud config server.I have enabled cloud bus with kafka and added required actuator endpoints to live reload properties on both server and client.
cloud:
bus:
enabled: true
management:
endpoints:
web:
exposure:
include: refresh, bus-refresh, beans, env
Issue is when I am using #value annotation to read property value. I am not getting updating value. (#value only get updated when I apply #RefreshScope,But I cannot use it because I have ton of client services and will to add #RefreshScope on all beans where properties are being used)
#Value("${message.testValue}")
private String info;
private String readValue() {
return info;
}
But when I read using Environment, I am getting updated value after refresh
private String readValue() {
return this.environment.getProperty("message.testValue");
}
What changes I need to implement to get updated value using #Value annotation withou #RefreshScope? Or there is something which I am missing in implementation.
Currently migrating my application to Micronaut 3, I encountered one problem with micronaut-gcp. Especially with the google secret manager. I am using gradle with Kotlin DSL.
Actual configuration: (not working, using plugin io.micronaut.library version 2.0.4)
gradle 7.2
micronaut 3.0.1
Previous configuration: (working with no plugin, using micronaut-bom)
gradle 6.5.1
micronaut 2.4.0
micronautGcp 3.5.0
I/ The Problem
My goal is to load some secrets as key/value pairs into a property source.
I followed the documentation that says to use a bootstrap.yml as follows:
micronaut:
config-client:
enabled: true
gcp:
secret-manager:
keys:
- MY_SECRET
Then in my class I should be able to inject that secret value using the #Value annotation. Actually I am doing this in my tests. I am using the #MicronautTest annotation from jUnit5 so I have something that looks like this:
#MicronautTest
public class MyClass {
#Value("${my.secret}")
private String mySecret;
}
then comes the problem, when running any test, it fails in the initialization saying this:
Error instantiating bean of type [io.micronaut.gcp.secretmanager.client.DefaultSecretManagerClient]
Message: getTransportChannel() called when needsExecutor() is true
Path Taken: new DistributedPropertySourceLocator(ConfigurationClient configurationClient,Duration readTimeout) --> new DistributedPropertySourceLocator([ConfigurationClient configurationClient],Duration readTimeout) --> new DefaultCompositeConfigurationClient([ConfigurationClient[] configurationClients]) --> new SecretManagerConfigurationClient([SecretManagerClient secretManagerClient],SecretManagerConfigurationProperties configurationProperties) --> new DefaultSecretManagerClient([SecretManagerServiceClient client],Environment environment,GoogleCloudConfiguration googleCloudConfiguration,ExecutorService executorService)
io.micronaut.context.exceptions.BeanInstantiationException: Error instantiating bean of type [io.micronaut.gcp.secretmanager.client.DefaultSecretManagerClient]
II/ What I've tried ?
When I've seen that, I was thinking that maybe the bootstrap.yaml thing doesn't work the I tried to use the lower lever access to secret manager using injecting SecretManagerServiceClient as follows:
#MicronautTest
public class MyClass {
#Inject
private SecretManagerServiceClient client;
private void someTest() {
String mySecret = client.getSecret("MY_SECRET");
}
}
but got same error Message: getTransportChannel() called when needsExecutor() is true with same path taken.
I also tried to upgrade to micronaut 3.0.1 but didn't change anything.
III/ My solution to handle this
As my problem was to retrieve a secret during the testing stage of my ci/cd process and, as I am using Google Cloud Build. I could pass the secret using the availableSecrets feature in my cloudbuild.yaml:
steps:
...
- name: 'gradle'
entrypoint: 'bash'
args: ['./gradlew', 'test']
secretEnv: ['MY_SECRET']
...
availableSecrets:
secretManager:
- versionName: projects/PROJECT_ID/secrets/MY_SECRET/versions/latest
env: 'MY_SECRET'
then passing it in my application.yml:
my:
secret: ${MY_SECRET}
and then in my code:
#MicronautTest
public class MyClass {
#Value("${my.secret}")
private String mySecret;
}
But I dont find this solution very satisfying as I find the bootstrap.yaml one more convenient.
If someone gets a solution for me or an idea/advice, I'll be happy to take it.
Have a nice day !
I've been down that rabbit hole. Long story short I got past this by upgrading the google-cloud-secretmanager dependency from 1.6.4 to e.g. 2.0.2
Like so:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-secretmanager</artifactId>
<version>2.0.2</version>
</dependency>
Yesterday I started with upgrade from Spring Brussels-SR3 to Spring Brussels-SR6.
The Spring Boot goes from 1.5.4. to 1.5.9, Jackson goes from 2.8.8 to 2.8.10). I am using HATEOAS and HAL links. It means my Jackson configuration looks like this:
#Configuration
public class JacksonConfiguration {
private static final String SPRING_HATEOAS_OBJECT_MAPPER = "_halObjectMapper";
#Autowired
#Qualifier(SPRING_HATEOAS_OBJECT_MAPPER)
private ObjectMapper springHateoasObjectMapper;
#Primary
#Bean(name = "objectMapper")
ObjectMapper objectMapper() {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer(DateTimeFormatter.ISO_INSTANT));
springHateoasObjectMapper.registerModules(javaTimeModule);
springHateoasObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
springHateoasObjectMapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
springHateoasObjectMapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
springHateoasObjectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
springHateoasObjectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
springHateoasObjectMapper.enable(SerializationFeature.INDENT_OUTPUT);
springHateoasObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return springHateoasObjectMapper;
}
}
It means I am reusing the _halObjectMapper bean and adding some more configurations. It had been working until I started with the upgrade.
What is wrong?
The thing that goes wrong is that after the upgrade all my serialization customizations and HAL conventions are not applied- datetime formats, indenting HAL "_links" JSON field changes to "links" ... So the _halObjectMapper is not used for serialization any more.
Any clue what could be the issue or where should I dig to figure out what is wrong?
Additional info after some debugging:
I have figured out that TypeConstrainedMappingJackson2HttpMessageConverter that uses _halObjectMapper is not used for conversion to json anymore. The reason is that it does not get to the collection of converters when starting spring. It looks like it is not created for RequestMappingHandlerAdapter bean because of some condition that skips creation when Jackson2HalModule is allready registered in some other converter (in this case ProjectingJackson2HttpMessageConverter).
Any idea what could be a cause or where to look at to figure out why the spring boot start proceeds differently?
Additional info after some more debugging:
The difference I see before and after the upgrade is that before the upgrade the ProjectingJackson2HttpMessageConverter was populated with new instance of ObjectMapper. But after the upgrade, the ObjectMapper is resolved from container so the _halObjectMapper is chosen. As a result the ProjectingJackson2HttpMessageConverter matches as a converter with registered halModule and TypeConstrainedMappingJackson2HttpMessageConverter creation is ommited for RequestMappingHandlerAdapter.
One more interesting thing is that there are two more microservices I upgraded. The difference is that one has the same issue and one is working. The one that is working has different spring security and oauth2 setup. Bean of class OAuth2RestTemplate is not defined in the microservice that is working. Microservices with OAuth2RestTemplate have the issue. The reason why I am pointing this out is that there is different in the initialization behavior in these two cases. The OAuth2RestTemplate rest template is populated with these converters too and it might affect the initialization process.
Temporary solution
As a temporary hotfix I have downgraded spring-data-commons from 1.13.6.RELEASE to 1.13.6.RELEASE. However the newer code makes more sense to me.
I am still trying to achieve some better understanding and figure out correct approach
I don't know if it is helpfull to you, but I had a very similar problem with a Spring Boot upgrade from Version 2.0.3 to 2.0.4. I still don't know what exactly caused the problem, but the solution was to create Beans for every Module I use instead of replacing the default ObjectMapper. In your case it would look something like this:
#Configuration
public class JacksonConfiguration {
#Bean
JavaTimeModule javaTimeModule () {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer(DateTimeFormatter.ISO_INSTANT));
return javaTimeModule;
}
}
All Features can be set via the applications.properties file like this:
spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=false
spring.jackson.deserialization.READ_DATE_TIMESTAMPS_AS_NANOSECONDS=false
and so on. For more information on how to configure the default object mapper without actually replacing it see https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper and https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
I am building Spring Boot app with Apache CXF . While trying to add swagger I get No operations defined in spec! error, though I specified via annotations 1-2 opeations.
The part of CXF config for Swagger is below:
factory.setFeatures(Collections.singletonList(createSwaggerFeature()));
public Swagger2Feature createSwaggerFeature() {
Swagger2Feature swagger2Feature = new Swagger2Feature();
swagger2Feature.setPrettyPrint(true);
swagger2Feature.setSupportSwaggerUi(true);
swagger2Feature.setScanAllResources(true);
swagger2Feature.setHost("localhost:8080");
swagger2Feature.setBasePath("/cxf/todo_list");
swagger2Feature.setTitle("TodoList Application");
swagger2Feature.setContact("https://www.github/abondar24");
swagger2Feature.setDescription("Another TodoList application with Spring Boot and Swagger");
swagger2Feature.setVersion("1.0.0");
return swagger2Feature;
}
I believe feature isn't set correctly.What am I missing?
The link for api-docs is like this: http://localhost:8080/cxf/todo_list/api-docs?url=/cxf/todo_list/swagger.json
In explore field on UI page: /cxf/todo_list/swagger.json
I have solved the problem. I forgot to put #Path annotation to my REST-service. After I did it swagger started working. The only thing I don't understand - how to get rid of default tag
I too had the same problem,
I fixed it by Modifying controller module
Here i changed from #Controller => to #RestController and it worked.
I'm attempting to build code into our base pom which autoconfigures Spring Cloud Config server lookup through Eureka. We're doing this to avoid templating .yml properties for developers building microservices. For example, we want to java config all behavior triggered from these properties:
spring:
application:
name: MyMicroservice
cloud:
config:
enabled: true
server:
prefix: /diagnostics/admin/config
failFast: true
discovery:
enabled: true
serviceId: echo
management:
context-path: /diagnostics/admin
eureka:
password: password
client:
serviceUrl:
defaultZone: http://user:${eureka.password}#localhost:8761/eureka/
instance:
leaseRenewalIntervalInSeconds: 10
statusPageUrlPath: /diagnostics/admin/info
healthCheckUrlPath: /diagnostics/admin/health
After much experimenting, the following approach mostly works except for the Eureka-discovered config server (resulting in no overridden config properties):
#Order(-1)
public class AdditionalBootstrapPropertySourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
Map<String, Object> theBootstrapYmlConfig = new HashMap<>();
theBootstrapYmlConfig.put("spring.cloud.config.enabled", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.server.prefix", "/diagnostics/admin/config");
theBootstrapYmlConfig.put("spring.cloud.config.failFast", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.discovery.enabled", new Boolean(true));
theBootstrapYmlConfig.put("spring.cloud.config.discovery.serviceId", "echo");
theBootstrapYmlConfig.put("management.context-path", "/diagnostics/admin");
theBootstrapYmlConfig.put("eureka.client.serviceUrl.defaultZone", "http://user:password#localhost:8761/eureka/");
theBootstrapYmlConfig.put("eureka.instance.leaseRenewalIntervalInSeconds", new Integer(10));
theBootstrapYmlConfig.put("eureka.instance.statusPageUrlPath", "/diagnostics/admin/info");
theBootstrapYmlConfig.put("eureka.instance.healthCheckUrlPath", "/diagnostics/admin/health");
return new MapPropertySource("myExtraBootstrap", theBootstrapYmlConfig);
}
}
And I seem to need this Bean as well:
#ConditionalOnWebApplication
#Configuration
#Import(EurekaClientAutoConfiguration.class)
public class WorkfrontDiscoveryClientConfigServiceBootstrapConfiguration {
#Bean
#ConditionalOnClass({ DiscoveryClient.class, ConfigServicePropertySourceLocator.class })
#ConditionalOnMissingBean
DiscoveryClientConfigServiceBootstrapConfiguration discoveryClientConfigServiceBootstrapConfiguration() {
DiscoveryClientConfigServiceBootstrapConfiguration discoveryClientConfigServiceBootstrapConfiguration =
new DiscoveryClientConfigServiceBootstrapConfiguration();
return discoveryClientConfigServiceBootstrapConfiguration;
}
}
Finally, I put both into spring.factories to ensure they are constructed. The problem is that the PropertySourceLocator is never used to construct the call within ConfigServicePropertySourceLocator to retrieve the properties. No matter what I do, I cant seem to match the behaviors that specifying the properties within bootstrap.yml would produce.
Edit 4 days later
The key factor (and restriction) here is the ability to look up the config server through Eureka. In the current spring cloud release (1.0.2), the property source is retrieved and constructed too early in the spring initialization cycle for the config-lookup-through-eureka java property source config I have above. Plus if the Eureka server is slow or not available at bootstrap startup time, the Config server property source is never reconstructed when Eureka finally comes up. This in my mind is a bug.
I solved this all by eliminating the concept of looking up the config server through Eureka, and requiring this minimum config in bootstrap.yml:
spring:
application:
name: MyMicroservice
cloud:
config:
uri: http://localhost:8888/diagnostics/admin/config
eureka:
client:
serviceUrl:
defaultZone: http://user:password#localhost:8761/eureka/
and then setting the rest in the java AdditionalBootstrapPropertySourceLocator
Edit 30 days later
Java-configing bootstrap properties continues to be a challenge. I'm doing this because I'm developing a framework without templating or code generation (the premise of spring boot). I've added spring-retry to the mix and client-to-config gets retried but re-registration to Eureka does not. This is why Eureka-first had to be abandoned for me. I'd put my vote in for integrating spring-retry into the Eureka registration process so I can go back to Eureka-first for my framework. Still on Spring Cloud 1.0.2.
Edit 100 days later
Update for where we ended up. Continuing along our mantra of avoiding property templating, enforcing policies and practices within code .. and continuing without a Eureka-first concept, we abandoned PropertySourceLocator and simply used a SpringApplicationRunListener as follows:
public class OurFrameworkProperties implements SpringApplicationRunListener {
:
public void started() {
if (TestCaseUtils.isRunningFromTestCase()) {
System.setProperty("spring.cloud.config.failFast", "false");
System.setProperty("spring.cloud.config.enabled", "false");
System.setProperty("eureka.client.enabled", "false");
} else {
// set production values same way
}
}
}
A caution that this started() actually gets called twice inside the spring code (once not passing any program arguments btw) everytime your Spring application runs or gets an Actuator refresh().
If your PropertySourceLocator is listed inspring.factories (I assume as a BootstrapConfiguration) then it needs to be a #Component (or maybe even a #Configuration).
you have to set this properties in the boostrap.properties
eureka.instance.metadataMap.configPath: /your-app-name
and comment this one
#spring.cloud.config.uri=http://localhost:8888/
and obviously it must be also this
eureka.client.serviceUrl.defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
eureka.client.instance.preferIpAddress: true
according with the documentation
https://cloud.spring.io/spring-cloud-config/multi/multi__spring_cloud_config_client.html#discovery-first-bootstrap