Spring PropertySource with dynamic values - spring

I have a custom PropertySource which contains a list of "dynamic" properties.
DynamicPropertySource extends AbstractPropertySource<DynamicProperty>
The values of these properties gets updated at regular intervals. The intention is to be able to use these the same as any other Spring property.
The property can then be used like this
#Value("${my.dynamic.property}") DynamicProperty myProperty;
someMethod(myProperty.getValue());
Spring then complained that there was no converter for DynamicProperty -> String which I then added using
environment.getConversionService().addConverter(...)
And I also had to add a converter for String -> DynamicProperty
This works great when retrieving the property manually and the value contained in DynamicProperty gets updated perdiodically.
environment.getProperty("my.dynamic.property", DynamicProperty.class)
But when injecting with #Value, Spring retrieves the DynamicProperty instance, converts it to a String using the registered converter, then converts it back to a DynamicProperty using the registered converter. This means that it is now not the same instance as contained in the PropertySource, and the value does not get updated along with the value in the PropertySource. As a hack I tried to look up the property from the DynamicPropertySource in the converter, but the converter does not receive the property name so there is no easy way to do that.
Is there any way I can get Spring to not convert DynamicProperty -> String -> DynamicProperty when using #Value ?

Related

String.format returns null in Spring Boot after retrieving information from application.properties

I have a Spring Boot application, using Intellij, and am trying to use the #Value annotation in order to get an environment variable from my application.properties.
My application.properties looks like this
server.port=27019
web.entrance.id=63d284ec
Using debugger, I can see that the value of entranceId is successfully retrieved from application.properties, but the same variable is always null in the String.format and my WebUrl has the string 'null' in it and I don't understand why.
#RestController
public class Controller {
#Value(("${entrance.id}"))
private String entranceId;
String WebUrl = String.format("http://localhost:27019/%s", entranceId);
Can someone explain if there is some detail I'm missing why this happens?
Thank you
Your thinking is wrong. Spring will process the #Value after the object has been constructed, whereas your field is being initialized as soon as the class is being constructed (so during constructing).
Basically what Spring does
new Controller()
Detect #Value and with reflection set value
Any construction callbacks (like #PostConstruct).
Your field is being filled at step 1 not after step 2. At which point the #Value hasn't yet been processed.
If you want to set the value you need to do that in an #PostConstruct method or use constructor injection to construct the URL.

Spring REST input validation

I'm writing a REST service using Spring Boot and JPA. I need to be able to validate some of the input fields and I want to ensure I'm using a proper pattern for doing so.
Let's assume I have the following model and I also have no control over the model:
{
"company" : "ACME"
"record_id" : "ACME-123"
"pin" : "12345"
"company_name" : ""
"record_type" : 0
"acl" : ['View','Modify']
"language" : "E"
}
The things I need to do are:
Ensure the value is not empty - This seems simple enough using the #NotEmpty annotation and I can pass a message.
Ensure the value is part of a valid list of values - The example here is the language property in the model above. I want the value to be either E,F or S. This seems possible using a custom annotation (eg #ValidValue({"E","F","S"})) but is there a better/"Springy" way to do this?
Ensure the values in a list are part of a valid list of values - The example here is the acl property. Again this seems possible with a custom annotation like #ValidListValues({"View", "Modify", "Delete", "Hide"}) but same question as above.
Set a default value - From what I read, custom validator annotations are only able to validate and not modify. I would like to do something like #DefaultValue(value=5) if the value is null. Is this possible? More on this below.
Set a default value to the return of a static method - For example if the pin field in model above isn't set, I want to set it to something like Util.getRandomDigitsAsString(5).
Use values from another property - I would like to validate that one property contains the string from another property. Using the example model, I want to ensure that record_id starts with company.
I have this setup in what I believe is a standard way with the controller -> service -> DTO -> DAO -> Model. Another option I was thinking about was creating a method in the validateCreate() that would go through all of the items above and throw an exception if needed.
Thanks.
Yes, NotEmpty is the right way
You should define a Language enum. The language field of your POJO should be of type Language
Same as 2. Define an Acl enum.
Define that in your Java code. Initialize the value of the field to 5 by default. If the JSON contains a value, Jackson will set the field value to the value in the JSON. Otherwise, it will stay as 5. Or initialize the field to null, and add a method getValueOrDefault(int defaultValue) that returns the default value you want if the value is null.
Same as 4
Define a custom validator that applies on the class itself, rather than a property of the class. In the validator chec that the two related values are correct.

Missing Converter when using Spring LdapTemplate with Grails Validateable annotation

I'm using the Spring LDAP (docs) library in a Grails application. I have a class annotated with the #Entry annotation, so it is mapped to an LDAP server. This all works quite beautifully.
However, when I add the Grails #Validateable annotation (to enable validating the LDAP class similarly to Grails domain classes) and attempt to retrieve data from LDAP (i.e. a findAll operation on the LdapUserRepo, or similar), I get the following exception:
Message: Missing converter from class java.lang.String to interface org.springframework.validation.Errors, this is needed for field errors on Entry class com.ldap.portal.LdapUser
Basically, it seems like the AST transformation performed by the #Validateable annotation is producing extra fields (namely the errors field) on the LdapUser object. It appears that Spring LDAP, in processing the #Entry logic, assumes a default mapping for the fields property (probably interpreting it as a string field on the LDAP object). When it gets nothing from the LDAP server, it attempts to set the field of type ValidationErrors to a value of type String -- an empty string.
I did some looking in github and found this code that seems relevant and may support my theory.
My question is: is this behavior expected for annotations, and how can one prevent fields added by one annotation from being inappropriately processed by another annotation?
At present the best workaround I've come up with for my specific issue is to add an errors field to my LdapUser object and mark it as transient (so that LDAP ignores it):
#Transient
ValidationErrors errors

Using a custom ObjectMapper for Spring XD Json to Java Conversion

Is there an easy way to convert a JSON payload to a Java object using a custom ObjectMapper (Jackson) or do I have to provide a custom type converter. I know that I could use a processor, but somehow it would be nice to use input and output types of the stream definition.
In the second case: Am I even able to provide a custom type converter for application/json to Java?
The documentation states: "The customMessageConverters are added after the standard converters in the order defined. So it is generally easier to add converters for new media types than to replace existing converters."
I bet that there is an existing "application/json" converter - but at a first glance I could not find further information if it is even possible to replace existing converters.
Thanks!
Peter
If you look at streams.xml You can see the relevant configuration. The configured lists are used to construct a CompositeMessageConverter which visits every MessageConverter in list order until it finds one that can do the conversion and returns a non-null result. A CompositeConverter instance is created for each module instance that is configured for conversion (i.e., defines an inputType or outputType value) by filtering the list of candidate message converters, which all inherit AbstractFromMessageConverter. The list is paired down to those which respond true to public boolean supportsTargetMimeType(MimeType mimeType) (where mimeType is the value of the input/outputType). The CompositeMessageConverter is injected into the corresponding MessageChannel and converts the payload.
There are a couple of things you can do. You can override the xd.messageConverters bean definition. For example, you can replace JsonToPojoMessageConverter and PojoToJsonMessageConverter with your own subclasses. You can also insert your own implementations in the list before the above converters and have your implementation match only specific domain objects for which you need a custom JSON mapper.
Another possibility is to define your own mime type and provide converters for that mime type as customMessageConverters. In any case, follow these guidelines forextending Spring XD

Spring #Value and dynamically modifying values at runtime (PlaceholderConfigurerSupport)

This is a question about customisation of Spring's placeholder resolution in #Value annotations.
We initialise all properties in our app using #Value, normally from servlet context init params, eg:
web.xml
<context-param>
<param-name>app.some.param</param-name>
<param-value>SOME_VALUE</param-value>
</context-param>
Class file
#Value("${app.some.param:DEFAULT_VALUE}")
private String myParameter;
We actually don't use web.xml, we use Tomcat context files or even specify using vmargs.
What we'd like to support is dynamic changes to these properties at runtime. I want to somehow collect a list of property keys that are used in #Value and which also have a new annotation like #Dynamic. For properties marked as #Dynamic the bean may provide a corresponding setter, to do any re-initialisation when the property is modified.
I would then like to create a service that supports updating the property by key, eg:
void setProperty(String key, String value) {
// find all beans that have #Value and #Dynamic and set field or call setter
// NB - should support Spring type coercion, eg. string --> integer, boolean, list, etc.
}
I've been looking at the source for PlaceholderConfigurerSupport and BeanDefinitionVisitor. It seems I might be able to override PlaceholderConfigurerSupport.doProcessProperties and create a custom BeanDefinitionVisitor, but there is quite a lot of code to wade through. I wondered if anyone had looked at this before and found a solution.
I should note that there's more we ultimately want to do. We want to persist changed properties in a backing store, and use these instead of the configuration on startup if they've been modified. In this way we'd have a hierarchy of property sources: default in code, context/property files, peristed config that's been modified. We also want to provide a UI showing a set of all dynamic properties. You get the idea.
Thanks

Resources