pass variable to a method as a parameter - java

I have following code in my tml file:
<t:loop source="navItem.subPages" value="var:subPage">
<t:any element="li" class="prop:classForPageName">
<t:pagelink page="var:subPage">${getMenuPageName(var:subPage)}</t:pagelink>
</t:any>
</t:loop>
I have a problem to pass a variable var:subPage to method ${getMenuPageName(var:subPage)}, as this throws an exception:
Could not convert 'getMenuPageName(var:subPage)' into a component parameter binding: Error parsing property expression 'getMenuPageName(var:subPage)': line 1:15 no viable alternative at input '('.

You can't use binding prefixes (like var:) inside property expressions.
You may only use prefix in front of the expression to let Tapestry know how it should interpret the remainder (the part after the colon).
Refer to NBF grammar for property expressions to see what's allowed inside:
Tapestry Documentation > User Guide > Property Expressions.
Property expressions were created to support just very basic constructs. If you need more complex expressions you should create corresponding methods in your java class and refer to them using the prop: binding prefix.
Template expansions you've mentioned (${...}) work the same as parameter bindings:
Under the covers, expansions are the same as parameter bindings. The
default binding prefix for expansions is "prop:" (that is, the name of
a property or a property expression), but other binding prefixes are
useful, especially "message:" (to access a localized message from the
component's message catalog).

Related

Spring Boot: using SpEL collection selection from YAML in #ConditionalOnExpression

I have a Spring Boot application with a YAML configuration that contains a feature list like this:
features:
- key: feature1
enabled: true
description: First feature
- key: feature2
enabled: false
description: Second feature
...
I would like to use #ConditionalOnExpression to conditionally initialize beans related to those features, identifying them by keys. Since "features" property is a list, it seems I need collection selection to do this. I have tried these two options for the annotation's value:
#ConditionalOnExpression("${features.?[key == 'feature1'][0].enabled}")
#ConditionalOnExpression("${features}.?[key == 'feature1'][0].enabled")
But both give the same error on startup:
org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
If I pass the expression (without ${}) to SpelExpressionParser.parseExpression() and then evaluate it (against a list of feature objects built programmatically), it works as expected and returns the value of "enabled" property. So the expression's structure seems to be OK, and the problem is how I use it in #ConditionalOnExpression. What exactly can I be doing wrong?
Spring SpEL supports only simple property placeholders inside the properties file.
If your properties file and condition code are like this, your SpEL will work:
features:
feature1:
enabled: true
description: First feature.
feature2:
enabled: false
description: Second feature.
#ConditionalOnExpression(
value = "#{'${features.feature1.enabled}'.equals('true')}"
)
But if you want to load it as a map feature and use the map SpEL features;
The most elegant way is writing the key-values as an inline JSON (use ' and " chars to avoid cumbersome escaping) and parsing it using SpEL:
features: '{
"feature1": {"enabled":"true", "description":"..."},
"feature2": {"enabled":"false", "description":"..."}
}'
#ConditionalOnExpression(
value = "#{${features}['feature1']['enabled'].equals('true')}"
)
If you had used the selection method it would have looked like this;
#ConditionalOnExpression(
value = "#{${features}.?[key=='feature1']['feature1']['enabled'].equals('true')}"
)
${...} has nothing to do with SpEL; that syntax is for property placeholders in Spring.
e.g. #Value("${property.foo:default}" String someProperty.
This will set someProperty to the value of property.foo if present, or default otherwise. (the :... part can be omitted to force the property to exist).

in:query and in:header parameters in yaml file

I'm learning creating rest Api using spring boot. FOr reference I was checking some existing code where in yaml file I found two parameters mentioned below
name: "name"
in: "query"
description: "doing something"
x-phase-dataelem: "CONF//Y/N"
required: false
schema:
type: string
maxlength:15
name: "tame"
in: "header"
description: "doing something something"
x-phase-dataelem: "CONF//Y/N"
required: true
schema:
type: string
maxlength:15
am literally not able to understand these parameters
in: "query"
in: "header"
x-phase-dataelem: "CONF//Y/N"
I know that, these are some values which are being passed to client url to process, but not able to understand these parameters. what's significance of using these 3 parameters ?
can anyone help ?
This YAML snippet looks like a Swagger/OpenAPI contract. You can find more about OpenAPi and read its specification here - https://swagger.io/specification/
in describes the location of the HTTP parameter.
Quote from the OpenAPI specification:
There are four possible parameter locations specified by the in
field:
path - Used together with Path Templating, where the parameter value is actually part of the operation's URL. This does not include
the host or base path of the API. For example, in /items/{itemId}, the
path parameter is itemId.
query - Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
header - Custom headers that are expected as part of the request. Note that RFC7230 states header names are case insensitive.
cookie - Used to pass a specific cookie value to the API.
Regarding the x-phase-dataelem, it is a custom extension in your OpenAPI contract. It is used for providing some additional metadata/information/properties about the described items (including parameters).
Quote from the OpenAPI specification:
While the OpenAPI Specification tries to accommodate most use cases,
additional data can be added to extend the specification at certain
points.
The extensions properties are implemented as patterned fields that are always prefixed by x-, for example, x-internal-id. The value can be null, a primitive, an array or an object. Can have any valid JSON format value.

How does Spring interpolate ${x} inside a string?

I have a java class in a Spring project that looks (edited) like:
#Component
public class X
{
private static final ApplicationContext CTX = new FileSystemXmlApplicationContext("file:${PATH}/ApplicationContext.xml");
...
I am looking for the reference explaining how the ${PATH} is interpolated in the string parameter. The PATH is passed as a system property ( java -DPATH=...) so I assume it takes it from there but I can't find an explanation describing the mechanism. Is it a Spring related feature similar to the syntax used in #Value?
configLocations (type String) passed to one of the FileSystemXmlApplicationContext constructors are processed by the resolvePath() method inherited from the AbstractRefreshableConfigApplicationContext class.
resolvePath() documentation says:
Resolve the given path, replacing placeholders with corresponding environment property values if necessary. Applied to config locations.
See Also:
PropertyResolver.resolveRequiredPlaceholders(String)
resolveRequiredPlaceholders() documentation says:
Resolve ${...} placeholders in the given text, replacing them with corresponding property values as resolved by getProperty(java.lang.String). Unresolvable placeholders with no default value are ignored and passed through unchanged.
The PropertyResolver declaring that getProperty() method is actually a StandardEnvironment.
StandardEnvironment documentation says:
Environment implementation suitable for use in 'standard' (i.e. non-web) applications.
In addition to the usual functions of a ConfigurableEnvironment such as property resolution and profile-related operations, this implementation configures two default property sources, to be searched in the following order:
system properties
system environment variables

NiFi custom processor expression language

I'm trying to make a custom processor in Apache NiFi that can add an attribute/string to the JSON object in the flowfile content. At the moment it works when I just use a string but it's not working when I use NiFi's expression language although I have it supported in my code.
The expression language is 100% correct as it works in another processor and I've also tried different attributes to make sure it's not the attribute.
The property:
public static final PropertyDescriptor ADD_ATTRIBUTE = new PropertyDescriptor
.Builder().name("Add Attribute")
.description("Example Property")
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.build();
Later in my code when I want to get the value and put in the JSON object I use:
jsonObject.put("hostname", context.getProperty(ADD_ATTRIBUTE).evaluateAttributeExpressions().getValue());
I also made a Unit Test and it works when I assign a text value to the testrunner.setProperty. However I don't know how I can assign an attribute to the testrunner or how I can use expression language in my test.
Thanks in advance for any suggestions or a solution!
I'll put my answer from Hortonworks Community Connection here too FWIW:
If the expression refers to attributes on a flow file, you will need to pass a reference to the flow file into evaluateAttributeExpressions:
FlowFile flowFile = session.get();
jsonObject.put("hostname", context.getProperty(ADD_ATTRIBUTE).evaluateAttributeExpressions(flowFile).getValue());
If the property contains an attribute name (rather than an Expression containing an attribute name) and you want the value from the flow file:
jsonObject.put("hostname", flowFile.getAttribute(context.getProperty(ADD_ATTRIBUTE).getValue()));
If the attribute value itself contains Expression Language and you want to evaluate it, take a look at the following class:
org.apache.nifi.attribute.expression.language.Query
Regarding testing...
Assuming you are evaluating the expression language against an incoming FlowFile (evaluateAttributeExpressions(flowFile)) then you can do the following:
runner.setProperty(ADD_ATTRIBUTE, "${my.attribute}");
Then create an attribute Map that has my.attribute in it:
final Map<String,String> attributes = new HashMap<>();
attributes.put("my.attribute", myAttribute);
Then enqueue some content with the attributes:
runner.enqueue(fileIn, attributes);
runner.run();
An example from the code base:
https://github.com/apache/nifi/blob/1e56de9521e4bc0752b419ffc7d62e096db1c389/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.java#L243

Spring 4 #Value where property default is a java system property

In Spring 4, using the #Value annotation, what is the right way to specify a system property as a default if a specified property does not exists?
Whereas this works for the no-default case:
#Value("${myapp.temp}")
private String tempDirectory;
This doesn't work when I need a default:
#Value("#{myapp.temp ?: systemProperties.java.io.tmpdir}")
private String tempDirectory;
Nor does this:
#Value("#{myapp.temp ?: systemProperties(java.io.tmpdir)}")
private String tempDirectory;
Both of these give me an exception at the time Spring is trying to create the bean:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'configurationService': Invocation of init method failed;
nested exception is java.lang.NullPointerException
Can this be done?
I tried the following and it worked for me:
#Value("${myapp.temp:#{systemProperties['java.io.tmpdir']}}")
private String tempDirectory;
The missing parts for you I believe was not using ?: and needing the #{}. According to this answer:
${...} is the property placeholder syntax. It can only be used to dereference properties.
#{...} is SpEL syntax, which is far more capable and complex. It can also handle property placeholders, and a lot more besides.
So basically what is happening is we are telling Spring to first interpret myapp.temp as property placeholder syntax by using the ${} syntax. We then use : instead of ?: (which is called the Elvis operator) since the elvis operator only applies to Spring Expression Language expressions, not property placeholder syntax. The third part of our statement is #{systemProperties['java.io.tmpdir']} which is telling Spring to interpret the next expression as a Spring Expression and allows us to get system properties.
Try systemProperties['java.io.tmpdir'].
It's a map, so if the key has a dot in the name, you should use [..]
For me it only works with different property-names (being property.name.a a key with a value in my application.properties and property.name.b a Environment-Variable) like:
#Value("${property.name.a:${property.name.b}}")
The same names didn´t work for me like expected (loading the default, when the first property isn´t present), e.g.:
#Value("${property.name.a:${property.name.a}}")

Categories