I'm writing an OSGi component with cardinal values associated with it.
I've written the following lines:
#Property(name="cardinalValue",cardinality=4,description="testing cardinality")
private String[] cardinalValue;
Then in the code I'm trying to print out the cardinal value:
for(String cardinal : cardinalValue){
log.debug(cardinal);
}
Once I install and trigger method in my component in the OSGi bundle, it appears that there is a null pointer exception even after I fill the values in the configuration manager console in AEM console.
It is the first time that I'm working with cardinality and I have even tried the List and Vector approach same as the array approach above, but still wasn't successful in printing the values in the log.
I would suggest you to initialize your property yourself.
For this change current property declaration in next way:
#Property(label="cardinalValue",cardinality=4,description="testing cardinality")
private static final String CARDINAL_PROPERTY = "cardinalValue";
private String[] cardinalValue;
Then write method which will initialize property with values from configuration on component Activation and Modification:
#Activate
#Modified
protected void activate(final Map<String, Object> props) {
String[] cardinalValue = PropertiesUtil.toStringArray(props.get(CARDINAL_PROPERTY));
}
Now your cardinalValue variable will initialized.
Related
I am creating a spring.config.import factory and can't see how to access properties from the invoking(parent) document
# mish-mash of properties without a common prefix
key1 = value1
key2 = value2
# now I want to read key1, key2 inside the factory
spring.config.import = myfactory:
Inside my implementation
#Configuration
public class MyFactoryResolver implements ConfigDataLocationResolver<MyResource>, Ordered {
#Override
public List<MyResource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
// how do I get the values or key1 and key2
// tried
binder.bind("key1", String.class).get(); // FAIL
// key1 needs to be in a higher source like environment KEY1=VALUE1
}
// tried as field
#Value("${key1}")
String key1; // always null
// cannot inject; too early in life-cycle ?
}
Now spring-cloud-client can do this
spring.cloud.config.username = johndoe
spring.cloud.config.password = john's password
spring.config.import = configserver:http://myserver.com
— so clearly it is possible to read a property value from the parent doc.
But I can't see how this code works — this code is baroque/mysterious to me:
it doesn't read a property like spring.cloud.config.username simply
instead it creates a sidekick #ConfigurationProperties ClientConfigProperties bean (how?) that wraps all spring.cloud.config.* key/values. This bean can't be injected (too early in the life-cycle??), so it is retrieved from a Binder and then all the properties are available; so if these properties can be
read off a sidekick, why I can't I read them easily...
my properties don't have a standard prefix so it is not easy to create a #ConfigurationProperties sidekick
...also in the code of spring-cloud-config-client you sometimes see new ClientConfigProperties(...) — I always thought this was forbidden in DI-land as the container won't be able to manage it for you.
TL;DR - what I am looking for is a way to read property/key values from the context(binder?) of the document or a sister document that invoked the factory; without having to create a side-kick bean and forcing all properties to confirm to prefix-naming. (This is a legacy application where property names were not enforced...).
Update: I attemped to duplicate the sidekick pattern — standardised property names to a prefix and one segment, created a holder #ConfiguationProperties bean and added it as an EnableAutoConfiguration factory (copy-pasta from spring-cloud-config). Copied code:
private MyProperties resolveHook(ConfigDataLocationResolverContext context) {
// TODO Auto-generated method stub
boolean registered = context.getBootstrapContext().isRegistered(MyProperties.class);
System.out.println("RESOLVER: MyProperties is registered = " + registered);
if (registered) {
return context.getBootstrapContext().get(MyProperties.class);
}
Binder localBinder = context.getBinder();
BindHandler localHandler = context.getBootstrapContext().getOrElse(BindHandler.class, null);
System.out.println("RESOLVER: BindHandler is null? " + (localHandler == null));
BindResult<MyProperties> object = localBinder.bind(MyProperties.PREFIX,
Bindable.of(MyProperties.class), localHandler);
System.out.println("RESOLVER: object is bound? " + (object.isBound()));
if (object.isBound()) {
MyProperties properties = object.get();
context.getBootstrapContext().registerIfAbsent(MyProperties.class, InstanceSupplier.of(properties));
System.out.println(
"RESOLVER: register object of type " + (properties.getClass().getName()) + " " + properties);
return properties;
}
return null;
}
Holy cow - this actually works - the sidekick bean is created and all the fields are injected from the parent document. Then the resolver can read off the property values — however this is surely the most obscure and round-about way of doing things, and there has to be a simpler method?
Credit for the answer goes to the Spring Team (philwebb at GH) when I posted this as a question to the Spring repository https://github.com/spring-projects/spring-boot/issues/32854:
I mistakenly used the binder that can be constructor injected when implementing a ConfigDataLocationResolver. Instead one should use the binder from the first parameter of the overriden methods:
// Constructor
public MyResolver(Log log, Binder binder) {
super();
this.log = log;
// don't use this object to access properties
// from the invoking properties file
this.binder = binder;
}
#Override
boolean isResolvable(ConfigDataLocationResolverContext context...) {
Binder localBinder = context.getBinder();
// this object has access to properties defined in the invoking
// parent application.properties
}
TL;DR there are number of binder objects one can access (constructor injection, from a context) — one should be using the Binder from the context and not the constructor.
Since I've added an integer to my Schedule class, Gson is throwing an error on some devices: java.lang.IllegalStateException: Expected an int but was BEGIN_ARRAY at line 1 column Y (e.g. column 112 or 120 etc). I looked at this and this answer, which seems to suggest Gson is expecting an int but is getting a BEGIN_ARRAY char, but I have no idea why this would happen after the refactor of adding an extra int to the class.
Before this, my code to parse the list of Schedule objects from a stored Json string was working perfectly fine. I added the Since annotation because of the exception being thrown. Here's the Schedule class:
public class Schedule {
/**
* Added this variable
*/
#Since(1.1) private int addedVar;
/**
* All other variables have the #Since(1.0) annotation
*/
#Since(1.0) all other vars;
}
The function to parse the schedules:
public static ArrayList<Schedule> schedulesFromJson(String schedulesJson) {
Type listType = new TypeToken<ArrayList<Schedule>>(){}.getType();
Gson gson = new Gson();
try {
return gson.fromJson(schedulesJson, listType);
} catch (Exception exception) {
// Try to use the previous version of the schedule, because of IllegalStateException
gson = new GsonBuilder().setVersion(1.0).create();
return gson.fromJson(schedulesJson, listType);
}
}
The strange thing is: on some devices (like my own test devices), this crash never happened. Because of the crash, I added the Since annotation with the try and catch clause, since I expected it might have to do with the extra integer being added and could prevent that by simply reading in the old Schedule version, but this is still throwing the same exception in the catch clause.
Any help with why this is happening?
Figured it out: Because Proguard wasn't set up to not obfuscate the Schedule object (thanks #Marcono1234 for tipping me in the right direction), the Schedule object was stored in storage as an obfuscated object ({"a":true,"b":"Name","c":[true,true,true,true,true,false,false], etc}) instead of using the variable names.
The Exception was thrown because, based on the Schedule class structure before adding the addedVar, there was an array in the schedule. Easier with example.
The old schedule class:
public class Schedule {
private boolean isActive;
private String scheduleName;
private boolean[] days;
private final long timeCreated;
private ArrayList<String> list;
}
The new schedule class:
public class Schedule {
private boolean isActive;
private String scheduleName;
private boolean[] days;
private final long timeCreated;
private int addedVar; // <-- Here it goes wrong
private ArrayList<String> list;
}
Because of adding the int variable before the ArrayList<String> list, when Gson tried to deserialize the stored JSON String, it expected to see an int (the addedVar, but instead saw BEGIN_ARRAY, from the list.
I fixed it by placing the `addedVar`` after the list in my Schedule class, so the Since(1.0) annotation will properly work.
i need to do something like this
String myVar = "myString";
...
#Preauthorize("customMethod(myVar)")
public void myMethod() {
...
}
but I'm failing at it. How can I do that? It says it cannot be resolved
EDIT:I'm decoupling few rest services and sometimes I have to share infos between them
#Value("${my-properties}")
String urlIWantToShare;
...
#PreAuthorize("isValid(#myValue,urlIWantToShare)")
#RequestMapping(value = "**/letsCheckSecurityConfig", method = RequestMethod.GET)
public boolean letsCheckSecurityConfig(#RequestHeader(name = "MY-VALUE") String myValue)) {
return true;
}
this "isValid" custom security method will call an external service, that doesn't know anything about the caller and his infos. I need to transmit few infos and I need to take them from different kind of sources
One of the sources is my application.properties
EDIT2: I managed to do this
#PreAuthorize("isValid(#myValue, #myProperty)")
#RequestMapping(value = "**/letsCheckSecurityConfig", method = RequestMethod.GET)
public boolean letsCheckSecurityConfig(#RequestHeader(name = "MY-VALUE") String myValue,
#Value("${my-property-from-app-properties}") String myProperty))
..but I want to use not only actual static properties but runtime one. Any help?
You can create a wrapper method without parameters which will call the desired method with parameters. In the annotation you can use the method without parameters
Apologies if I have misunderstood what you are trying to do, but from my understanding you're trying to set an annotation at runtime based on a variable / app.properties, so that you can then read this variable and then execute your class?
If this is the case, You cannot do this from an annotation alone as annotations cannot read local variables and cannot be set at runtime.
However, one option for you is to have an object which contains the 'values' of interest for you and then read the values from the object.
Something like the below:
PoJo
public class testObject{
#test
private String myVar;
private String myValue;
//Getters and Setters
}
Get Object values
public void getFields (Object obj){
Field fields = obj.getClass().getDeclaredFields();
for (Field f : fields){
test fieldAnnotation = f.getAnnotation(test.Class);
if (fieldAnnotation != null){
f.get(obj);
// Do checks based on this
}
}
}
Main Class
public static void main(String[] args){
//Create object
testObject test = new testObject();
test.setOne("testOne");
test.setTwo("testTwo");
getFields(test);
}
I've pulled this code based on what I had to do to get the fields - but in my case, I did not know the object types I was going to be passed. You are simply using the annotation to 'mark' the fields you want to retrieve and then reading the value from the object.
If you're in a similar situation, then you can see my answer here: initial answer
Let me know if i've misunderstood this and I can try and further clarify my answer.
SonarQube reported 'Make "ids" transient or serializable' for this line of code:
private final List<String> ids;
So I changed it to:
private final ArrayList<String> ids;
and made sure that my public interface (the constructor in this case) still used just an interface:
public MyClass(List<String> ids) {
this.ids = (ids == null) ? new ArrayList<>() : new ArrayList<>(ids);
}
This got rid of the first SonarQube warning, but now it gives:
The type of the "ids" object should be an interface such as "List" rather than the implementation "ArrayList".
I don't want to turn off all rules for using interfaces rather than concrete classes, but only for cases like this.
You don't say what version of the Java plugin you're using, but it's likely pre-3.4; that rule was relaxed in v3.4 to ignore private fields.
I need to override a property value given in my property file while loading my JBOSS Application server.
I tried out with below code overriding processProperties() method in PropertyPlaceholderConfigurer.
My property file has this entry
base.url="defaultUrl"
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
protected String convertPropertyValue(String originalValue) {
return (originalValue != null) ? originalValue.trim() : originalValue;
}
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
super.processProperties(beanFactory, props);
for (Enumeration names = props.propertyNames(); names.hasMoreElements();) {
String key = (String) names.nextElement();
props.put("base.url", getUpdatedUrl());
}
}
}
I am injecting base.url value in a placeholder ${base.url} in application context.
How should I update the value of given property in run time. The above code always take the value in the property file not the updated value.
Blowing the dust off from this question. This can be done using PropertyPlaceholderConfigurer and adding a new property file (at the end of the list), where you put the properties you want to override. (The name/file path of the property file can contain an environment variable you pass at build time). Here's the javadoc of PropertiesLoaderSupport#setLocations:
Note: Properties defined in later files will override properties
defined earlier files, in case of overlapping keys. Hence, make sure
that the most specific files are the last ones in the given list of
locations.
As of Spring 5.2 this was deprecated in favor of PropertySourcesPlaceholderConfigurer:
Specialization of PlaceholderConfigurerSupport that resolves ${...}
placeholders within bean definition property values and #Value
annotations against the current Spring Environment and its set of
PropertySources.
Some examples here