How to overwrite config in a Quarkus extension - java

Is there a way to overwrite a configuration in a Quarkus extension with a hard-coded value?
What I'm trying to do: I am creating a custom Quarkus extension for JSON logging, based on quarkus-logging-json but with additional (non static) fields. I reuse some classes from the extension's runtime library, so it is a Maven dependency of the runtime module of the extension (and the deployment also needs to be declared as a dependency to my deployment module, because the quarkus extension plugin checks this).
It seems to work fine, except that I now have 2 formatters, and the following line is logged:
LogManager error of type GENERIC_FAILURE: Multiple console formatters were activated
I would like to disable the quarkus-logging-json extension completely by hard-coding these values:
quarkus.console.json.enable=false
quarkus.file.json.enable=false.
Is there a way to do this?
Thank you.

An extension cannot override runtime configuration values, it can however set a default value using io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem

Related

Possible to ignore configuration classes on the classpath?

I have a Spring Boot application that works as expected when ran with embedded tomcat, but I noticed that if I try to run it from an existing tomcat instance that I'm using with a previous project then it fails with a NoClassDefFoundError for a class that I don't use anywhere in my application.
I noticed in the /lib directory I had a single jar that contained a few Spring annotated classes, so as a test I cleaned out the /lib directory which resolved the issue. My assumption is that Spring is seeing some of the configurations/beans/imports on the classpath due to them existing in the /lib directory and either trying to autoconfigure something on its own, or is actually trying to instantiate some of these classes.
So then my question is - assuming I can't always fully control the contents of everything on the classpath, how can I prevent errors like this from occurring?
EDIT
For a little more detail - the class not being found is DefaultCookieSerializer which is part of the spring-session-implementation dependency. It is pulled into one of the classes in the jar located in /lib, but it is not any part of my application.
Check for features provided by #EnableAutoConfiguration. You can explicitly configure set of auto-configuration classes for your application. This tutorial can be a good starting point.
You can remove the #SpringBootApplication annotation from the main class and replace it with an #ComponentScan annotation and an #Import annotation that explicitly lists only the configuration classes you want to load. For example, in a Spring boot MVC app that uses metrics, web client, rest template, Jackson, etc, I was able to replace the #SpringBootApplication annotation with below code and get it working exactly as it was before, with all functional tests passing:
#Import({ MetricsAutoConfiguration.class,
InfluxMetricsExportAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class,
WebClientAutoConfiguration.class,
RestTemplateAutoConfiguration.class,
RefreshAutoConfiguration.class,
ValidationAutoConfiguration.class
})
#ComponentScan
The likely culprit of mentioned exception are incompatible jars on the classpath.
As we don't know with what library you have the issue we cant tell you the exact reason, but the situation looks like that:
One of Spring-Boot autoconfiguration classes is being triggered by the presence of class on the classpath
Trigerred configuration tries to create some bean of class that is not present in the jar you have (but it is in the specific version mentioned in the Spring BOM)
Version incompatibilities may also cause MethodNotFound exceptions.
That's one of the reasons why it is good practice not to run Spring Boot applications inside the container (make jar not war), but as a runnable jar with an embedded container.
Even before Spring Boot it was preferred to take account of libraries being present on runtime classpath and mark them as provided inside your project. Having different versions of the library on a classpath may cause weird ClassCastExceptions where on both ends names match, but the rest doesn't.
You could resolve specific cases by disabling autoconfiguration that causes your issue. You can do that either by adding exclude to your #SpringBootApplication or using a property file.
Edit:
If you don't use very broad package scan (or use package name from outside of your project in package scan) in your Spring Boot application it is unlikely that Spring Boot simply imports configuration from the classpath.
As I have mentioned before it is rather some autoconfiguration that is being triggered by existence of a class in the classpath.
Theoretical solution:
You could use maven shade plugin to relocate all packages into your own package space: see docs.
The problems is you'd have face:
Defining very broad relocation pattern that would exclude JEE classes that need to be used so that container would know how to run your application.
Relocation most likely won't affect package names used as strings in the Spring Boot annotations (like annotations #PackageScan or #ConditionalOnClass). As far as I know it is not implemented yet. You'd have to implement that by yourself - maybe as some kind of shade plugin resource processor.
When relocating classes you'd have to replace package names in all relevant configuration located in the jars. Possibly also merge some of those.
You'd also have to take into account how libraries that you use, or spring uses use package names or files.
This is definitely not a trivial tasks with many traps ahead. But if done right, then it would possibly allow you to disregard what is on the containers classpath. Spring Boot would also look for classes in relocated packages, and you wouldn't have those in ordinary jars.

SpringBoot library project does not see main app properties

I have a setup like this:
Main SpringBoot project with application-default.properties which on our deployment server are partially overwritten by a deployment specific properties.
Shared SpringBoot library project which has its own properties.
And when I run my main project with the library project attached (via gradle sourceControl gitRepository) I can see that the properties in the library project are empty.
How can I make the library project use the properties passed down from the main application ?
If you want to merge properties, please consider this official page.
Option 1 - default properties in library
As I found previously (probably, it is fixed), if you have jar1 and jar2 (sorted alphabetically) and both of them have application.properties file, only first will be used. They aren't merged. So please be carefully there.
However you can use #PropertySource in your library, e.g. put default properties there into the custom file name (for example - defaults-for-jar2.properties or something like this, to avoid automatic loading by Spring).
In this case:
Property load logic outside of your library will be the same with current.
Your library will load file from #PropertySource and next they will be overridden (if you have this) by your application.
Option 2 - configuration properties
If you use Kotlin and Spring, you can use ConfigurationProperties. And you can define the default values there. Moreover, IntelliJ Idea will highlight the default and possible values (according to the type, because you can use not only String, but any custom enum class, Duration class, etc.).
Just from that link:
#ConstructorBinding
#ConfigurationProperties("blog")
data class BlogProperties(var title: String, val banner: Banner) {
data class Banner(val title: String? = null, val content: String)
}
#SpringBootApplication
#EnableConfigurationProperties(BlogProperties::class)
class BlogApplication {
// ...
}
Please note:
You should mention your settings data class in the library configuration.
You should configure kapt properly to have Intelli Sence in IDEs.

How to override Spring Boot library properties?

I would like to override the library properties that I've written in project that is using it. I'm using this Spring guide on creating library: https://spring.io/guides/gs/multi-module/
I would like to know how to override for example my.properties file in the project that uses my library.
Is it even possible?
Do not add application.properties files to a library. It could cause a myriad of problems. If you want to set a default property do it like this:
#ConfigurationProperties("foo")
public class FooConfig {
private int bar = 999;
// getter / setter
}
Every application should configure the library values for itself in its own application.properties file.
Seems you cannot. Since Spring itself also cannot
In the sample above we have configured the service.message for the test using the default attribute of the #SpringBootTest annotation. It is not advisable to put application.properties in a library because there might be a clash at runtime in the application that uses it (only one application.properties is ever loaded from the classpath). You could put application.properties in the test classpath, but not include it in the jar, for instance by placing it in src/test/resources.
Spring Boot property order precedence is described here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
From the link:
Spring Boot uses a very particular PropertySource order that is designed to allow sensible overriding of values. Properties are considered in the following order:
The order is 1-17 then. For example, 4 on the list (command-line arguments) overrides 15 on the list (your application.properties file) and so on.
Depends on how the library property file specifies properties. If the properties are looked up from environment/system first, then you can override in your code. If hard-coded, then not.
For example:
prop=${ENV_VAR:abc}
You can set ENV_VAR environment variable, or env-var system variable in your code to override the value of prop. If you don’t, the default value abc will be used.

Spring-boot > implicitly auto register a 3rd party bean

This may be an impossible task, but here goes...
Is it possible to register a spring bean, by (ONLY) adding a jar to the classpath of a spring-boot application?
Scenario: I would like to create a non-intrusive plugin jar, which when imported into a spring-boot project's classpath, will automatically be picked up and provide a service (e.g. via a RestController).
Constraints
I don't want to change or reconfigure the existing spring-boot application (i.e. no additional scan paths or bean config).
I don't have any knowledge of the target spring-boot application's package structure/scan paths.
I guess I was hoping that by default Spring scan's its own package structure (i.e. org.springframework.** looking for the presence of database libs, etc) and I could piggy-back off that - I haven't had any luck (so far).
I've setup an example project in github, to further clarify/illustrate my example and attempts.
** Solution Addendum **
This bit that got it working, was to add the following file, which points to an #Configuration config file...
plugin-poc\src\main\resources\META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.thirdpartyplugin.PluginConfiguration
I think in such cases you would try to add a spring auto configuration that is annotated with #ConditionalOnClass to be only evaluated if the given class is on the classpath. This class can register the bean and would just be evaluated if the conditional evaluates to true
Here is the relevant part of the spring boot documentation : Creating your own auto-configuration

How do I configure log4j logging for a jar?

I have a project A with log4j.jar on its build path. I have a number of classes that have logging statements in the form of:
Logger _log = Logger.getLogger(A.<>.class);
...
_log.info("...");
I am exporting the project as a jar into another project B. Project B already has its own log4j jar and it's own .xml configuration file. I want to configure particular classes from A to log to Console Apender at varying "levels". How do I do this, please?
Well, basically, you shouldn't do that. Think of it this way: if that would be done that way, each library that is included in any application would host its own logging configuration, very potentially overriding any of those that are in the application, and each other, in non-specified order. You wouldn't want that. So do not.
[In case you really, really want to do it, you could have properties file in the jar that could be overriden by an xml file in the main application. See details here. But don't. :) ]
In general, you don't. In general, you want to have one log4j configuration file. But i suppose you could load your configuration explicitly from your code, as described in the log4j documentation
Pick a version of log4j.jar, probably the newer one. You can only use one version. You need to merge project A's and project B's logging configuration. You could do this in log4j classes at runtime, but don't. Just bite the bullet and merge them once in source control.

Categories