Apache Camel: How to properly nest a function? - spring

In Camel's Simple documentation they say :
From Camel 2.9 onwards you can nest functions, such as shown below:
<setHeader headerName="myHeader">
<simple>${properties:${header.someKey}}</simple>
</setHeader>
Here is what I am trying to do:
<simple>${property.${property.prefix}variableName}</simple>
I am trying to access the exchange property whose name is:
exchange.getProperty("prefix", String.class) + "variableName"
But I'm getting this exception:
Caused by: org.apache.camel.language.simple.types.SimpleIllegalSyntaxException: ${ cannot accept or at location 22
${property.${property.prefix}variableName}
What am I doing wrong here?
I'm using Camel version 2.13.0.

I confirm the following code works fine with Camel 2.13.0:
from("...")
.setProperty("prefix", constant("pre-"))
.setProperty("pre-variable", constant("value"))
.setHeader("myHeader", simple("${property.${property.prefix}variable}"))
You may have special character in the prefix property that prevents proper resolution.

Related

XQuery: Using xs:time($arg) vs cast as xs:time

In one of my projects I'm using
XQuery 3.0
Saxon HE 9.8 (transitive because of Camel)
Spring Boot 2.1.0
Apache Camel 2.22.0
I consume a XML message in which the following element occurs:
<mytimeelement></mytimeelement>
As you can see it is empty so I thought that the following XQuery-Expression would return an empty sequence:
$transaction/*:flags/*:mytimeelement
Unfortunately this seems not to be the case because calling the XQuery-Expression from above in an xs:time($arg) like:
xs:time($transaction/*:flags/*:mytimeelement)
does not return an empty sequence as I would have expected but instead returns an exception:
Invalid time "" (too short)
The thing is: I want to use the xs:time($arg) as validation that if a value is in the element it has to have the correct format, but if it's empty it doesn't matter. So I did this not only with xs:time but also with xs:date and xs:decimal.
My question now is: Why is the expression not returning an empty sequence but an empty string? Or should I better use a cast as xs:time instead?

SpelParseException: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'

I am trying to condtionally create a component using #ConditionalOnExpression("not ${service.synchronous} && not ${service.disabled}").
I based this on Spring Boot SpEL ConditionalOnExpression check multiple properties, which provides a multi-property conditional as follows: #ConditionalOnExpression("${properties.first.property.enable:true} && ${properties.second.property.startServer:false}")
However, I keep getting:
Caused by: org.springframework.expression.spel.SpelParseException: EL1041E: After parsing a valid expression, there is still more data in the expression: 'lcurly({)'
Those properties are always set in my .properties file so I did not provide a default value with the colon notation. What am I doing wrong?
You will need to provide the default values for your properties like in the example you followed, so update the expression to be:
#ConditionalOnExpression("not ${service.synchronous:false} && not ${service.disabled:true}")
In most such cases the properties your app is reading are not what you expect them to be.
Set a breakpoint on all constructors of SpelParseException. In the debugger you will see the expression that is parsed, that will give show you exactly which properties you are really using.
Maybe you have to go search a little in the stack until you find the right location where you can see the expression.
My mistake was that I had not imported the test properties file in a Spring test.
After I added #TestPropertySource("classpath:/application.properties") to the test class, the properties from the properties file were used.

ConstraintViolationException - extract field name which caused exception

I'm using hibernate-validator with a JAX-RS service to validate query parameters using #NotNull:
#GET
public Response doSomething(#NotNull #QueryParam("myParam") String myParam) {...}
This works as expected and throws a ConstraintViolationException if myParam is null. I'd like to extract the param name which is associated to the violation (e.g. myParam), and return that in the response message to the client but there does not appear to be an obvious way of extracting this from the exception. Can someone provide some insight?
As of BeanValidation 1.1 there is a ParameterNameProvider contract which makes parameter name extraction configurable. As mentioned in the other answer, with Java 8 you can get the parameter names in the byte code provided you compile with the -parameters flag. Use the ReflectionParameterNameProvider in this case. However, even with Java 7 you can get parameter names, for example by using the ParanamerParameterNameProvider. This parameter name provider is based on Paranamer and there are several ways to set it up.
This only works if you're using Java 8, as prior to Java 8 the actual parameter name was lost at compile time. Its now retained, assuming you compile and run at Java 8. See also http://docs.jboss.org/hibernate/validator/5.2/reference/en-US/html_single/#_java_8_support

Trying to migrate Rally Defect Requirement to JIRA

Using the Rally/JIRA connection I am trying to synchronise them.
https://help.rallydev.com/jira-5-installation-user-guide
I am trying to map the Requirement (UserStory) field for Defects to a custom field in JIRA.
Here are my field mappings
<FieldMapping>
<Field><Rally>Name</Rally> <Other>Summary</Other></Field>
<Field><Rally>Description</Rally> <Other>Description</Other></Field>
<Field><Rally>FormattedID</Rally> <Other>RallyKey</Other></Field>
<Field><Rally>Status</Rally> <Other>Status</Other></Field>
<Field><Rally>Priority</Rally> <Other>Priority</Other></Field>
<Field><Rally>Requirement</Rally> <Other>RallyUserStory</Other></Field>
</FieldMapping>
I am using the RallyReferenceFieldHandler to map this to a custom field
<RallyReferenceFieldHandler>
<FieldName>Requirement</FieldName>
<ReferencedFieldLookupID>FormattedID</ReferencedFieldLookupID>
</RallyReferenceFieldHandler>
I am getting this error message:
RallyEIF::WRK::JiraRestConnection.block in validate - Field Handler field_name Requirement not found
would you mind posting more of your config with all of your field mappings and field handlers?
My only guess at the moment is that your is inside your block. If it were in the wrong place, you might get an error like you are seeing. It should be inside a block - for example:
<Connector>
<FieldMapping>
<Field><Rally>FormattedID</Rally><Other>OtherFormattedID</Other></Field>
<Field><Rally>Requirement</Rally><Other>RallyUserStory</Other></Field>
</FieldMapping>
<RallyFieldHandlers>
<RallyReferenceFieldHandler>
<FieldName>Requirement</FieldName>
<ReferencedFieldLookupID>FormattedID</ReferencedFieldLookupID>
</RallyReferenceFieldHandler>
<RallyFieldHandlers>
<OtherFieldHandlers>
<!-- Other Field Handlers here -->
</OtherFieldHandlers>
</Connector>

Spring, property file, empty values

I have configured spring security with a ldap server (but continue reading, it's not a problem if you have no knowledge about it, this is really a spring problem). All runs like a charm. Here is the line I use for that:
<ldap-server ldif="" root="" manager-dn="" manager-password="" url="" id="ldapServer" />
If I fill ldif and root attributes, it will run an embeded server:
<ldap-server ldif="classpath://ldap.ldif" root="dc=springframework,dc=org" manager-dn="" manager-password="" url="" id="ldapServer" />
If I fill other fields, it will run a distant server:
<ldap-server ldif="" root="" manager-dn="dc=admin,dc=springframeworg,dc=org" manager-password="password" url="ldap://myldapserver.com/dc=springframeworg,dc=org" id="ldapServer" />
All this stuff run correctly. Now I want to use Spring mechanism to load such parameters from a property file:
So I replace attribute values like this:
<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}" id="ldapServer" />
and create a property file with:
ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=
ldap.ldif.path=
ldap.ldif.root=
Now, the funny part of the problem. If I fill the following properties in the file:
ldap.server.url=ldap://myldapserver.com/dc=springframeworg,dc=org
ldap.server.manager.dn=dc=admin,dc=springframeworg,dc=org
ldap.server.manager.password=password
ldap.ldif.path=
ldap.ldif.root=
It runs a distant server as expected.
If I fill the property file like this:
ldap.server.url=
ldap.server.manager.dn=
ldap.server.manager.password=
ldap.ldif.path= classpath:ldap.ldif
ldap.ldif.root= dc=springframeworg,dc=org
It does not run, complaining that the ldap url is missing. But the problem is that if I change the spring configuration from:
<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="${ldap.server.url}" id="ldapServer" />
to (by just removing the reference to the variable ${ldap.server.url})
<ldap-server ldif="${ldap.ldif.path}" root="${ldap.ldif.root}" manager-dn="${ldap.server.manager.dn}" manager-password="${ldap.server.manager.password}" url="" id="ldapServer" />
It runs !
My thoughs are that spring does not replace the attribute value with the property config one if this one is empty. But I find it strange.
Can you give me some clue to understand that ? And what's the best to do to configure my ldap server via a property file ?
EDIT: this is due to a poor design choice (look at accepted answer), an issue has been opened on jira :
https://jira.springsource.org/browse/SEC-1966
Ok, I think this is a spring security bug.
If I debug and look at the class LdapServerBeanDefinition, there is a method called "parse". Here is an extract:
public BeanDefinition parse(Element elt, ParserContext parserContext) {
String url = elt.getAttribute(ATT_URL);
RootBeanDefinition contextSource;
if (!StringUtils.hasText(url)) {
contextSource = createEmbeddedServer(elt, parserContext);
} else {
contextSource = new RootBeanDefinition();
contextSource.setBeanClassName(CONTEXT_SOURCE_CLASS);
contextSource.getConstructorArgumentValues().addIndexedArgumentValue(0, url);
}
contextSource.setSource(parserContext.extractSource(elt));
String managerDn = elt.getAttribute(ATT_PRINCIPAL);
String managerPassword = elt.getAttribute(ATT_PASSWORD);
if (StringUtils.hasText(managerDn)) {
if(!StringUtils.hasText(managerPassword)) {
parserContext.getReaderContext().error("You must specify the " + ATT_PASSWORD +
" if you supply a " + managerDn, elt);
}
contextSource.getPropertyValues().addPropertyValue("userDn", managerDn);
contextSource.getPropertyValues().addPropertyValue("password", managerPassword);
}
...
}
If I debug here, all variables (url, managerDn, managerPassword...) are not replaced by the value specified in the property file. And so, url has the value ${ldap.server.url}, managerDn has the value ${ldap.server.manager.dn} and so on.
The method parse creates a bean, a context source that will be used further. And when this bean will be used, place holders will be replaced.
Here, we got the bug. The parse method check if url is empty or not. The problem is that url is not empty here because it has the value ${ldap.server.url}. So, the parse method creates a context source as a distant server.
When the created source will be used, it will replace the ${ldap.server.url} by empty value (like specified in the property file). And....... Bug !
I don't know really how to solve this for the moment, but I now understand why it bugs ;)
I cannot explain it, but I think you can fix your problem using defaulting syntax, available since Spring 3.0.0.RC1 (see).
In the chageg log you can read: PropertyPlaceholderConfigurer supports "${myKey:myDefaultValue}" defaulting syntax
Anyway, I think that the problem is because "" is valid value, but no value in the property file don't.
I think that url="" works because url attribute is of type xs:token in spring-security XSD and empty string is converted to null (xs:token is removing any leading or trailing spaces, so "" can be recognized as no value). Maybe the value of ${ldap.server.url} is resolved as empty string and that is why you've got an error.
You can try use Spring profiles to define different configurations of ldap server (see Spring Team Blog for details about profiles)
I believe there is an issue here while using place holders. The following will most probably solve the problem:
Create a class which extends PropertyPlaceHolderConfigurer and override its method convertPropertyValue()
in the method you can return the property as empty string if you find anything other than a string which is of type LDAP url i.e. ldap://myldapserver.com/dc=springframeworg,dc=org
Also you need to configure your new specialization of class PropertyPlaceHolderConfigurer in the context file.
Hope this helps.
You can define empty String in the application.properties file as following:
com.core.estimation.stopwords=\ \

Resources