Custom PropertyPlaceholderConfigurer doesn't resolve embedded property values - java

I have a Spring 4.3 custom PropertyPlaceholderConfigurer that does extra processing on values read from the properties files before injection:
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
#Override
protected String convertPropertyValue(final String originalValue) { ... }
#Override
protected String resolveSystemProperty(final String key) {
return convertPropertyValue(super.resolveSystemProperty(key));
}
}
My problem is that bean fields that are annotated for injection with property values, eg. #Value("${some_property}"), are in fact injected with the raw value read from the property source without my custom processing.

After some debugging I found that I should have also overiden com.ec.survey.security.SafePasswordPropertyPlaceholderConfigurer#resolvePlaceholder

Related

Is there a way to validate #Value in spring boot?

I'm using #Value to inject parameters from my properties file to variable in my application.
Because I'm not the only user in the application and I want to make the injection safety I need to validate the parameter before the injection.
Example:
properties file:
example.string=str1,str2
app.java
#Value(${example.string})
public String example;
the expected behavior in this case for example is to throw an exception because I assume "," id delimiter in array case
I don't think you can directly with #Value. But you could do something like this, which will fail on startup if validation fails:
#Validated
#ConfigurationProperties(prefix="my.prefix")
public class AppProperties {
//Validation annotations here
//#NotEmpty
//#MyCustomValidation
private String exampleString;
// getters / setters
}
I don't think you can do this before the injection, try to use the post construct method
you can do some thing like that :
#PostConstruct
public void validateValue() {
if (someProperty.contains(",")) {
throw new MyException("error");
}
}

SpringBoot configuring BeanNameGenerator to use Qualifier names

I am using proguard to obfuscate my code and to tackle duplicate bean definition names, I am using custom bean name generator to register beans with fully qualified names.
public static class CustomGenerator implements BeanNameGenerator {
#Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return definition.getBeanClassName();
}
}
Then I am creating application context using the custom name generator
ApplicationContext ctx = new SpringApplicationBuilder(DataQualityApplication.class)
.beanNameGenerator(new CustomGenerator())
.run(args);
The issue however is that the beans are now being registered using their corresponding class types and not by the qualifier names provided in the #Qualifier or #Component.
How can I achieve this in my project?
Solve it by extending AnnotationBeanNameGenerator rather than implementing the BeanNameGenerator interface
public static class CustomGenerator extends AnnotationBeanNameGenerator {
#Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
UnaryOperator<String> fun=pkgName->{
int lastIndex = pkgName.lastIndexOf ('.');
if (lastIndex!=-1){
pkgName=pkgName.substring (0, lastIndex);
}
return pkgName;
};
String className = super.generateBeanName(definition, registry);
String packagename = definition.getBeanClassName();
return (fun.apply(packagename) + "." + className);
}
}
This returns fully qualified names with qualifier/component name if annotated otherwise class name.
Solves the duplicate bean definition error in Proguard jar.
If you use the class name as bean name, that means you can have only one bean per type. In this situation, the injection can be done by type only, just remove the #Qualifer and don't name your beans, this should work fine. If you have several beans for the same class, you can't use your generator.

Unable to resolve variable from properties file when tried to access as function parameter using #Value annotation

This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}

Dynamic expression variables for validation messages in Spring MessageSource

I am using Hibernate Validator and a MessageSource to localize violation messages.
class FooValidator implements ConstraintValidator<ValidFoo, Foo>
#Override
public void initialize(ValidTransitions constraintAnnotation) {
}
#Override
public boolean isValid(Foo foo, ConstraintValidatorContext context) {
//some calculations
String result = calculations.toString();
HibernateConstraintValidatorContext hibernateContext = context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext.addExpressionVariable("invalidResult", result)
.buildConstraintViolationWithTemplate("Invalid field: ${invalidResult}")
.addConstraintViolation();
return false;
}
}
I want to have something like this in messages.properties:
ValidFoo.foo=Localized invalid field message, field: ${invalidResult}
But I can't find a way to inject dynamic parameters from my FooValidator to message property.
Named parameters and expression variables can be resolved only by Hibernate.
Spring uses MessageFormat.
See AbstractMessageSource.getMessageInternal for details.

DeltaSpike custom ConfigSource with CDI

I am trying to define a custom DeltaSpike ConfigSource. The custom config source will have the highest priority and check the database for the config parameter.
I have a ConfigParameter entity, that simply has a key and a value.
#Entity
#Cacheable
public class ConfigParameter ... {
private String key;
private String value;
}
I have a #Dependent DAO that finds all config parameters.
What I am trying to do now, is define a custom ConfigSource, that is able to get the config parameter from the database. Therefore, I want to inject my DAO in the ConfigSource. So basically something like
#ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {
#Inject
private ConfigParameterDao configParameterDao;
....
}
However, when registering the ConfigSource via META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource, the class will be instantiated and CDI will not work.
Is there any way to get CDI working in this case?
Thanks in advance, if you need any further information, please let me know.
The main problem is, that the ConfigSource gets instantiated very early on when the BeanManager is not available yet. Even the JNDI lookup does not work at that point in time. Thus, I need to delay the injection/lookup.
What I did now, is add a static boolean to my config source, that I set manually. We have a InitializerService that makes sure that the system is setup properly. At the end of the initialization process, I call allowInitialization() in order to tell the config source, that the bean is injectable now. Next time the ConfigSource is asked, it will be able to inject the bean using BeanProvider.injectFields.
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
#Inject
private ConfigParameterProvider configParameterProvider;
#Override
public int getOrdinal() {
return 500;
}
#Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
I have a request-scoped bean that holds all my config variables for type-safe access.
#RequestScoped
public class Configuration {
#Inject
#ConfigProperty(name = "myProperty")
private String myProperty;
#Inject
#ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
When injecting the Configuration class in a different bean, each ConfigProperty will be resolved. Since my custom DatabaseConfigSource has the highest ordinal (500), it will be used for property resolution first. If the property is not found, it will delegate the resolution to the next ConfigSource.
For each ConfigProperty the getPropertyValue function from the DatabaseConfigSource is called. Since I do not want to retreive the parameters from the database for each config property, I moved the config property resolution to a request-scoped bean.
#RequestScoped
public class ConfigParameterProvider {
#Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
#PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
I could sure change the request-scoped ConfigParameterProvider to ApplicationScoped. However, we have a multi-tenant setup and the parameters need to be resolved per request.
As you can see, this is a bit hacky, because we need to explicitly tell the ConfigSource, when it is allowed to be instantiated properly (inject the bean).
I would prefer a standarized solution from DeltaSpike for using CDI in a ConfigSource. If you have any idea on how to properly realise this, please let me know.
Even though this post has been answered already I'd like to suggest another possible solution for this problem.
I managed to load properties from my db service by creating an #Signleton #Startup EJB which extends the org.apache.deltaspike.core.impl.config.BaseConfigSource and injects my DAO as delegate which I then registered into the org.apache.deltaspike.core.api.config.ConfigResolver.
#Startup
#Singleton
public class DatabaseConfigSourceBean extends BaseConfigSource {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private #Inject PropertyService delegateService;
#PostConstruct
public void onStartup() {
ConfigResolver.addConfigSources(Collections.singletonList(this));
logger.info("Registered the DatabaseConfigSourceBean in the ConfigSourceProvider ...");
}
#Override
public Map<String, String> getProperties() {
return delegateService.getProperties();
}
#Override
public String getPropertyValue(String key) {
return delegateService.getPropertyValue(key);
}
#Override
public String getConfigName() {
return DatabaseConfigSourceBean.class.getSimpleName();
}
#Override
public boolean isScannable() {
return true;
}
}
I know that creating an EJB for this purpose basically produces a way too big overhead, but I think it's a bit of a cleaner solution instead of handling this problem by some marker booleans with static accessors ...
DS is using the java se spi mechanism for this which is not CD'Injectable'. One solution would be to use the BeanProvider to get hold of your DatabaseConfigSource and delegate operations to it.

Categories