Accessing camel properties in DSL - apache-camel

I have properties defined in camel context xml as follows. These properties will vary for different calls coming from API's exposed.
<util:properties id="abc-properties">
<prop key="serviceId"></prop>
<prop key="name"></prop>
</util:properties>
<propertyPlaceholder location="ref:abc-properties" id="properties" />
In my Java DSL I am trying to access these properties as follows in 'from' and in 'to'
from(Identifier + ":" + abcName + "://{{serviceId}}")
to(Identifier + ":" + abcName + "://{{serviceId}}")
When I my DSL routes are formed, I see that for 'to' I don't get updated value of serviceId. Value which had come first time continues to appear for all subsequent calls. for 'from' I always get updated value for serviceId.
By referring post Use Exchange Property in Camel DSL "to" , I tried using recipientList but that too dosen't seems to be working.
Do I need to use any other syntax to access property in 'to' ?

Please try this:
from(
Identifier + ":" + abcName + "://{{serviceId}}"
).recipientList(
simple(Identifier + ":" + abcName + "://${properties:serviceId}")
)

Related

Powershell Script to Edit IIS File Extensions

Curious if someone could point me in the direction to how I could automate IIS Request Filtering File Extensions? I have used the following per Microsoft manuals, but when the extension is already set to false I get an error. Even though the manual states that changing the value from false to true will enable. Am I missing something very simple?
https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/fileextensions/
Start-IISCommitDelay
$fileExtensions = Get-IISConfigSection -CommitPath 'Default Web Site' -SectionPath 'system.webServer/security/requestFiltering' | Get-IISConfigCollection -CollectionName 'fileExtensions'
New-IISConfigCollectionElement -ConfigCollection $fileExtensions -ConfigAttribute #{ 'fileExtension' = '.asax'; 'allowed' = $true } -AddAt 0
Set-IISConfigAttributeValue -ConfigElement $fileExtensions -AttributeName 'applyToWebDAV' -AttributeValue $true
Stop-IISCommitDelay
New-IISConfigCollectionElement : Filename:
Error: Cannot add duplicate collection entry of type 'add' with unique key
attribute 'fileExtension' set to '.asax'
At line:5 char:1
+ New-IISConfigCollectionElement -ConfigCollection $fileExtensions -Con ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-IISConfigCollectionElemen
t], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Micr
osoft.IIS.Powershell.Commands.NewIISConfigCollectionElementCommand
Set-IISConfigAttributeValue : Object reference not set to an instance of an
object.
At line:7 char:1
+ Set-IISConfigAttributeValue -ConfigElement $fileExtensions -Attribute ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Set-IISConfigAttributeValue],
NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,Microsoft.IIS.Powe
rshell.Commands.SetIISConfigAttributeValueCommand
WARNING: No active transaction.
Never able to figure out why this process doesn't work as it should per the man docs from Microsoft, but found the configuration files for IIS and made an edited version and wrote a Powershell to move the original and place the edited version in the needed directories. Problem solved.

Mule 4 dynamic queries in the Database Connector

In my flow in Mule 4 I am trying to query a database for specific data.
For example I want to run a query like this:
SELECT * FROM mulesoft WHERE plant = CCCNNB;
The thing is both plant and CCNNB need to be dynamic. They will come through an API request. I can handle the value to be dynamic, but I get empty results whenever I try to make the field dynamic.
I first create a variable which stores the json from the request:
set-variable value="#[payload]" doc:name="Set Variable" doc:id="8ed26865-d722-4fdb-9407-1f629b45d318" variableName="SORT_KEY"/>
Request looks like this:
{
"FILTER_KEY": "plant",
"FILTER_VALS": "CCNNB"
}
Afterwards in the db connector I configure the following:
<db:select doc:name="Select" doc:id="13a66f51-2a4e-4949-b383-86c43056f7a3" config-ref="Database_Config">
<db:sql><![CDATA[SELECT * FROM mulesoft WHERE :filter_key = :filter_val;]]></db:sql>
<db:input-parameters ><![CDATA[#[{
"filter_val": vars.SORT_KEY.FILTER_VALS,
"filter_key": vars.SORT_KEY.FILTER_KEY
}]]]></db:input-parameters>
Replacing :filter_key with plant works but as soon as I try to make it dynamic I get nothing in the response. It does not fail though, response code is 200 but I get nothing inside it.
How can I make this work?
You can directly use the stored variables in the query itself.
Query Should be an expression in DataWeave.
#["SELECT * FROM $(vars.table) WHERE $(vars.SORT_KEY.FILTER_KEY) = :filter_val"]
<db:select config-ref="Database_Config">
<db:sql><![CDATA[#["SELECT * FROM $(vars.table) WHERE $(vars.SORT_KEY.FILTER_KEY) = :filter_val"]]]></db:sql>
<db:input-parameters ><![CDATA[#[{
"filter_val": vars.SORT_KEY.FILTER_VALS
}]]]>
</db:input-parameters>
</db:select>
There is another way also to read values from payload to build a dynamic query as below
#["SELECT * FROM mulesoft
WHERE " ++ vars.SORT_KEY.FILTER_KEY ++ " = '" ++ vars.SORT_KEY.FILTER_VALS ++ "'"]
Below is the XML that is created for this, as a POC
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:os="http://www.mulesoft.org/schema/mule/os"
xmlns:salesforce="http://www.mulesoft.org/schema/mule/salesforce"
xmlns:db="http://www.mulesoft.org/schema/mule/db"
xmlns:xml-module="http://www.mulesoft.org/schema/mule/xml-module"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd
http://www.mulesoft.org/schema/mule/os http://www.mulesoft.org/schema/mule/os/current/mule-os.xsd">
<http:listener-config name="HTTP_Listener_config1"
doc:name="HTTP Listener config"
doc:id="6d5de64b-1355-4967-9352-4b324f02c7ad">
<http:listener-connection host="0.0.0.0"
port="8081" />
</http:listener-config>
<db:config name="Database_Config" doc:name="Database Config"
doc:id="d5c4d49c-aef3-4d4a-a7b5-470da3354127">
<db:my-sql-connection host="localhost"
port="3306" user="root" password="admin123" database="Mysql" />
</db:config>
<flow name="testFlow"
doc:id="8cfea1b0-d244-40d9-989c-e136af0d9f80" initialState="started">
<http:listener doc:name="Listener"
doc:id="265e671b-7d2f-4f3a-908c-8065a5f36a07"
config-ref="HTTP_Listener_config1" path="test" />
<set-variable value="#[payload]" doc:name="Set Variable"
doc:id="265a16c5-68d4-4217-8626-c4ab0a3e38e5" variableName="SORT_KEY" />
<db:select doc:name="Select"
doc:id="bdf4a59c-0bcc-46ac-8258-f1f1762c4e7f"
config-ref="Database_Config">
<db:sql><![CDATA[#["SELECT * FROM mulesoft.mulesoft WHERE " ++ vars.SORT_KEY.FILTER_KEY ++ " = '" ++ vars.SORT_KEY.FILTER_VALS ++ "'"]]]></db:sql>
</db:select>
<ee:transform doc:name="Transform Message"
doc:id="72cbe69f-c52e-4df9-ba5b-dd751990bc08">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
payload]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
</mule>
Explanation of the Flow
I am using the payload that is in Question
Seting a variable name "SORT_KEY", value of this varibale is complete payload that we receive.
then creating a dynamic query inside the Db connector
using transform message sending the data as response, that we received from DataBase
So, there are several issues here. One, the creation of the sql statement.
You can do DW inside the DB:SELECT component if you want, as shown by previous answers. Either the
#["SELECT * FROM myTable" ++ vars.myWhere]
OR
#["SELECT * FROM myTable $(vars.myWhere)"]
work.
The problem you will run into is that DataSense doesn't like this. Without the literal text for the column names, DataSense can't figure out what columns to retrieve so it raises an error "Unable to resolve value for the prameter: sql". This leaves an error in your code, which always gives me angst. I wish Mulesoft would resolve this problem.
BTW, if you do dynamic SQL, you should STILL use input parameters for each value to avoid SQL injections.
I have an "Idea" posted here to fix the bogus error: https://help.mulesoft.com/s/ideas#0872T000000XbjkQAC

CXF WSDL namespace without slash

I generate a WSDL with Apache CXF 3.3.0 from Java classes, and I get this :
xmlns:tns="http://impl.service.ti.commun.metier.civitas.fr/"
I don't understand why tns ends with "/", so I check source Apache CXF, and i found that JaxWsImplementorInfo call PackageUtils (https://github.com/apache/cxf/blob/master/core/src/main/java/org/apache/cxf/common/util/PackageUtils.java) method which returns :
return "http://" + String.join(".", parts) + '/';
So, I have an end slash.
I would like get this:
xmlns:tns="http://impl.service.ti.commun.metier.civitas.fr"
How do that ?
Thanks a lot,

Camel cxfws producer, POJO format, throws "Get the wrong parameter size to invoke the out service" error

I have a similar situation as Camel CXF POJO mode using Java DSL where
I have a wsdl https://api.stage.eventcore.com/ReportService.asmx?WSDL
Created a wsdl2java library
Using camel cxf producer component, with POJO format to make a SOAP request.
Setting the operationname, operationnamespace as headers. cxfEndpoint is also configured accurately.
Below is the error I am getting for "GetReport" operation.
java.lang.IllegalArgumentException: Get the wrong parameter size to invoke the out service, Expect size 7, Parameter size 4.
Please check if the message body matches the CXFEndpoint POJO Dataformat request.
Here is the binding info for the operation I am dealing with.
<wsdl:operation name="GetReport">
<soap:operation soapAction="https://api.eventcore.com/GetReport" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
<soap:header message="tns:GetReportAuthTokenHeader" part="AuthTokenHeader" use="literal"/>
<soap:header message="tns:GetReportCredentialsHeader" part="CredentialsHeader" use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
<soap:header message="tns:GetReportAuthTokenHeader" part="AuthTokenHeader" use="literal"/>
</wsdl:output>
</wsdl:operation>
As part of the exchange body I am sending a list of objects with CredentialHeader, GetReport Object with necessary data. I also added holder parameters for Response message.
GetReport getReport = new GetReport();
getReport.setReportID(123);
getReport.setSortColumn("LastModified");
getReport.setStartRow(1);
getReport.setEndRow(2);
getReport.setSortAscending(true);
ReportFilter filter = new ReportFilter();
filter.setField("LastModified");
filter.setComparison(ComparisonType.GREATER_THAN);
filter.setMatchValue("2018-05-09T23:23:51.8769404Z");
filter.setMode(FilterMode.SELF);
getReport.setFilter(filter);
CredentialsHeader credentials = new CredentialsHeader();
credentials.setUserName("foo");
credentials.setPassword("bar");
credentials.setEventID(11111);
List<Object> params = new ArrayList<Object>();
params.add(getReport);
params.add(credentials);
params.add(null); //params.add(new AuthTokenHeader());
params.add(new javax.xml.ws.Holder<AuthTokenHeader>());
//params.add(new javax.xml.ws.Holder<GetReportResponse>());
exchange.getIn().setBody(params);
here is the cxfEndpoint configuration.
org.apache.camel.component.cxf.CxfEndpoint endpoint_cSOAP_1 = getCxfEndpoint(
"cxf://"
+ "https://api.stage.eventcore.com/ReportService.asmx"
+ "?dataFormat=POJO"
+ "&serviceClass="
+ "tableau.ea.eventcore.api.reportservice.ReportServiceSoap"
+ "&serviceName="
+ "{https://api.eventcore.com/}ReportService"
+ "&endpointName="
+ "{https://api.eventcore.com/}ReportServiceSoap"
+ "&defaultOperationNamespace="
+ javax.xml.namespace.QName.valueOf(
"{https://api.eventcore.com/}GetReport")
.getNamespaceURI()
+ "&defaultOperationName="
+ javax.xml.namespace.QName.valueOf(
"{https://api.eventcore.com/}GetReport")
.getLocalPart() + "&" + "loggingFeatureEnabled"
+ "=" + "true" + "&" + "continuationTimeout" + "="
+ 600000
+ "&headerFilterStrategy=#CXF_PAYLOAD_HEADER_FILTER"
+ "&properties.id=" + "cSOAP_1", false, false, false,
(String[]) null);
What I don't understand is , why cxf expecting 7 parameters? What are those 7 params? I tried making it 7 by adding some nulls, but it fails with "argument type mismatch" error. Please help me understand Input message parts in this particular operation.
I did workaround this issue by NOT using "-exsh true" option while generating wsdl, and setting the SOAP headers using Holders.LIST header. While doing that I had to set mustUnderstand = true for CredentialsHeader.
finally I got the headers added to the SOAP request. But, I really want to know why the path of passing all headers plus body as list of parameters ( with "-exsh true" option) didn't work. I had exact same approach working for a different webservice, but not for this one. I am curious what is making the difference.
Pls share if anyone has insight.

Camel Inter module coordination - dependency

My problem is I have a routeBuilder which does routing based on url patterns to different modules say A, B, D as follows (the customProcessor adds the ARestURN, BRestURN etc to the header which are comma seperated url patterns that will be matched against the in header CamelHttpUri to route to correct module)
from("servlet:///?matchOnUriPrefix=true")
.process(customProcessor)
.choice()
.when(simple("${in.headers.ARestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" + AUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false")
.when(simple("${in.headers.BRestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" + BUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false")
.otherwise()
.to("http4://" + DUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false");
My question is now I need to service a REST url request which requires me to acccess AUrl and BUrl and aggregate the results and then respond to the request how do I achieve this ?
I thought of writing something as below
from("servlet:///?matchOnUriPrefix=true")
.process(customProcessor)
.choice()
.when(simple("${in.headers.ARestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" + AUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false")
.when(simple("${in.headers.BRestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" + BUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false")
.when(simple("${in.headers.MultiModuleRestURN} contains ${in.headers.CamelHttpUri}"))
.to("direct:multimodule")
.otherwise()
.to("http4://" + DUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false");
from("direct:multimodule")
.process(new MyProcessor())
.to("http4://" + AUrl + "?bridgeEndpoint=true&throwExceptionOnFailure=false");
But I don't get howto get the results from first "AUrl" (which is a REST service returning json results) and do some process on it and provide specific values of the result to the next url which is BUrl to get results from it and process that and combine results and send it back to the calling service.
eg:
the REST call : /AB/123/getPrice
Need to pass 123 to Module A - REST call -> /A/123/getId response-{A: 123, id: x1}
Need to pass x1 to Module B - REST call -> /B/x1/getPrice response-{id: x1, p:$10}
Need to return to caller {name: 123, id: x1, price: $10 } (json combined object)
Update: Did as follows for now,
Note: removed bridgeEndpoint stuff for clarity
from("servlet:///?matchOnUriPrefix=true")
.process(customProcessor)
.choice()
.when(simple("${in.headers.multiRestURN} contains ${in.headers.CamelHttpUri}"))
.to("direct:multimodule")
.when(simple("${in.headers.ARestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" +AUrl)
.when(simple("${in.headers.BRestURN} contains ${in.headers.CamelHttpUri}"))
.to("http4://" +BUrl)
.otherwise()
.to("http4://" +DUrl);
//
from("direct:multimodule")
.to(ExchangePattern.InOut,"http4://"+AUrl)
.convertBodyTo(String.class)
.process(customAProcess)
.to(ExchangePattern.InOut, "http4://"+BUrl+"/resources")
.convertBodyTo(String.class)
.process(customBProcessor);
In customAProcess I do a
exchange.getIn().setHeader("AData", exchange.getIn().getBody(String.class));
So this data is available when BUrl request comes with result, I am sending to BUrl unnecessary data but this solves the issue hopefully, maybe when I get time I will look into enricher and aggregate strategy to solve the issue.
Enricher component might be what you are looking for. It's implementation of one of common patters. Please, note that it is possible to pass implementation of aggregation strategy via strategyRef parameter.
While implementing aggregation strategy for your first rest call you are able to enrich original exchange by returned data of your first rest response - see interface of AggregationStrategy, please. Just create beans of your strategies and configure enricher.
Hope it helps. Example context :) :
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="servlet:///?matchOnUriPrefix=true"/>
<enrich uri="http4://serviceA" strategyRef="strategyA"/>
<enrich uri="http4://serviceB" strategyRef="strategyB"/>
</route>
</camelContext>
<bean id="strategyA" class="..." />
<bean id="strategyB" class="..." />
You can do whatever you want with your exchanges, both original (being enriched) and new (enriching) in aggregation strategies.

Resources