Converting query parameter to Set - spring-integration

I am having trouble converting a query parameter in CSV format to a java.util.Set in my Spring Integration inbound gateway.
My outbound gateway adds the parameter like ?ids=[ID_1, ID_2].
My inbound gateway is reading the whole parameter as a single element into a Set like ["ID_1, ID_2"].
I have created my own static helper method to convert the String to a Set but wonder if there is a way to do the conversion implicitly in Spring Integration?
Thanks for your help!
My code is below.
Outbound Gateway:
<int-http:outbound-gateway
url="MY_URL?ids={ids}&foo={foo}"
request-channel="myChannel"
http-method="GET"
message-converters="myConverter"
header-mapper="myMapper"
expected-response-type="MyDto">
<int-http:uri-variable name="ids" expression="payload"/>
<int-http:uri-variable name="foo" expression="headers.foo"/>
</int-http:outbound-gateway>
Inbound Gateway:
<int:service-activator
input-channel="myChannel"
expression="#'myService'.getStuff(payload.ids, headers.foo)"/>
<int-http:inbound-gateway
id="myGateway"
request-channel="myChannel"
path="MY_URL"
message-converters="myConverter"
header-mapper="myMapper"
supported-methods="GET">
<int-http:header name="foo" expression="#requestParams.foo"/>
</int-http:inbound-gateway>
EDIT
This looks like it will solve my problem: https://docs.spring.io/spring-integration/docs/4.3.12.RELEASE/reference/html/messaging-endpoints-chapter.html#payload-type-conversion

Consider to use org.springframework.util.StringUtils.commaDelimitedListToSet():
/**
* Convert a comma delimited list (e.g., a row from a CSV file) into a set.
* <p>Note that this will suppress duplicates, and as of 4.2, the elements in
* the returned set will preserve the original order in a {#link LinkedHashSet}.
* #param str the input {#code String}
* #return a set of {#code String} entries in the list
* #see #removeDuplicateStrings(String[])
*/
public static Set<String> commaDelimitedListToSet(#Nullable String str) {

Related

Spring Batch: How to set the first line columns (header), of the csv file to token name for my file reader

I have a csv file to consume through spring batch. The column header (names) can differ based on the customer. When reading the data using FlatFileItemReader, how would i set the token name(s) to be the first line (header) column name(s) ? I am using Java Configuration to setup the flow.
Appreciate any comments/suggestions
One solution could be to discard the first line of the csv files with the FlatFileItemReader::setLinesToSkip method. Such as :
flatFileItemReader.setLinesToSkip(1);
You should set LineToSkip Property :
#Component
#JobScope
public class CsvToDbItemReader extends FlatFileItemReader<BillDTO> {
public CsvToDbItemReader(#Value("#{jobParameters}") Map jobParameters)
{
String uploadDate = (String) jobParameters.get("uploadDate");
this.setResource(new FileSystemResource(getFileName(uploadDate)));
this.setLinesToSkip(1); //set header line
LineMapper<BillDTO> lineMapper = createBillLineMapper();
this.setLineMapper(lineMapper);
}
}
You can use as below:
<bean id="flatFileItemReader"
class="org.springframework.batch.item.file.FlatFileItemReader">
<!-- linesToSkip => The number of lines to skip at the beginning of the
file. This feature is particularly useful to handle file headers. -->
<property name="linesToSkip" value="1" />

Extends Shopware Models

I need to extend Shopware variants models in order to add some custom attributes such as the type of metal,the type of stone a jewel, which is the base article.
These attributes will be used both in backend and frontend.
How can I do that? Thanks
Extending the Shopware core model itself is not possible at all. Depending on what specific model you are trying to extend there would be two different ways for some workaround:
If its the article itself that you want to extend you could use the custom attribute fields as described here: http://community.shopware.com/Anlegen,-Anpassen-und-Ausgabe-von-Artikel-Attributen_detail_1208.html
Another way would be to write a plugin where you create attribute fields by code on plugin install(). This is only possible on entities that do have an attribute table which belongs to the entity itself. For example s_order and s_order_attributes
For the second way create a method in your plugin's Bootstrap.php like the following and call the method in the plugin's install() method:
public function installOrderAttributes()
{
Shopware()->Models()->addAttribute(
's_order_attributes',
'ordermod',
'Random1',
'DECIMAL(12,4)',
false,
0.0000);
Shopware()->Models()->addAttribute(
's_order_attributes',
'ordermod',
'Random2',
'DECIMAL(12,4)',
false,
0.0000);
$metaDataCacheDoctrine = Shopware()->Models()->getConfiguration()->getMetadataCacheImpl();
$metaDataCacheDoctrine->deleteAll();
Shopware()->Models()->generateAttributeModels(array('s_order_attributes'));
}
The addAttribute() function in /engine/Shopware/Components/Model/ModelManager.php has the following signature:
/**
* Shopware helper function to extend an attribute table.
*
* #param string $table Full table name. Example: "s_user_attributes"
* #param string $prefix Column prefix. The prefix and column parameter will be the column name. Example: "swag".
* #param string $column The column name
* #param string $type Full type declaration. Example: "VARCHAR( 5 )" / "DECIMAL( 10, 2 )"
* #param bool $nullable Allow null property
* #param null $default Default value of the column
* #throws \InvalidArgumentException
*/
public function addAttribute($table, $prefix, $column, $type, $nullable = true, $default = null);
Hope this will help.
Kind regards!

Change dynamical directory on file:inbound-channel-adapter

I'm new with Spring and I'm using Citrus Framework.
I'll try to change, dynamically, the inbound-channel-adapter destination variable. This variable is located in properties file and change all the time.
Currently I'm using an AtomicReference and I change its value in java code
In context.xml :
<bean id="targetDir" class="java.util.concurrent.atomic.AtomicReference">
<constructor-arg value="${output.path.temp}"/>
</bean>
<file:inbound-channel-adapter id="fileInboundAdapter" auto-create-directory="false"
channel="fileChannel" directory="file:#targetDir.get()" auto-startup="false"
filename-pattern="*.xml">
<si:poller cron="0 * * * * ?"/>
</file:inbound-channel-adapter>
And in java file :
SourcePollingChannelAdapter fileInboundAdapter = (SourcePollingChannelAdapter)context.getApplicationContext().getBean("fileInboundAdapter");
if (fileInboundAdapter.isRunning()) {
fileInboundAdapter.stop();
#SuppressWarnings("unchecked")
AtomicReference<String> targetDir = (AtomicReference<String>)
context.getApplicationContext().getBean("targetDir", AtomicReference.class);
targetDir.set(strOutPath[0]+"/"+strOutPath[1]+"/"+strOutPath[2]+"/"+strOutPath[3]+"/");
fileInboundAdapter.start();
}
This solution don't works ... someone have any solutions ?
Thanks a lot.
That's true. Because your AtomicReference doesn't have affect to the target directory.
You do this directory="file:#targetDir.get()". It isn't correct at all, because this String will try to be converted to the File object. If you want to use here a SpEL it should be like this:
directory="#{targetDir.get()}"
without any file: prefix.
Anyway it doesn't help because that SpEL is evaluated only once at applicationContext strtup.
Since you are going to change the directory at runtime you should use FileReadingMessageSource.setDirectory from your service. Something like this:
SourcePollingChannelAdapter fileInboundAdapter = (SourcePollingChannelAdapter)context.getApplicationContext().getBean("fileInboundAdapter");
if (fileInboundAdapter.isRunning())
fileInboundAdapter.stop();
FileReadingMessageSource source = (FileReadingMessageSource) context.getApplicationContext().getBean("fileInboundAdapter.source");
source.setDirectory(new File(strOutPath[0]+"/"+strOutPath[1]+"/"+strOutPath[2]+"/"+strOutPath[3]+"/"));
fileInboundAdapter.start();
}
And get rid of that AtomicReference.
From the start you can use property-placeholder for the directory attribute directly.

'int-jdbc:stored-proc-outbound-gateway' is able to MAP ur Model element(s) by itself - is this a true statement?

Currently I am working with JDBC Spring Integration (to be very specific: <int-jdbc:stored-proc-outbound-gateway>) where the scenario is passing n-number of parameters to a stored procedure (ORACLE) and receive one return variable (with '1' or Error Message) along with n-number of CURSORS (sys_refcursor) which may have n-number of columns.
While working on this above scenario with Spring Framework I noticed the following issue(s):
My logic from front-end was, first read the 'return variable'. If that value is 1 then start reading all the CURSORS data or else through exception by redirecting user to the error page. Now, all my CURSORS were not having same number of columns as well as same length/type of data. And in middle layer I was having only one Model class to deal with all the CURSORS return elements. That was the challenge for me!
Previously I dealt with only one CURSOR. and therefore in middle layer I used Mapper class to map all CURSOR elements to my Model element's (getter and setter) to push data to the front. But then when used same concept to deal with n-number of CURSORS -that was truly a nightmare.
let me share what I did before and what I did later to resolve this situation at my end and then will draw my understanding for which will sick EXPARTS opinion at the later part of this post.
For one return variable (with '1' or Error Message) along with one CURSOR:
I had my Gateway definition as follows:
<!-- Stored Procedure Outbound-Gateway = To call a database stored procedure -->
<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-personalinfo"
request-channel="procedureRequestChannel"
data-source="dataSource"
stored-procedure-name="pkg_personalinfo_spring.proc_personalinfo_spring"
expect-single-result="false"
ignore-column-meta-data="true">
<!-- Parameter Definitions -->
<int-jdbc:sql-parameter-definition name="firstname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="lastname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="p_RetVal" direction="OUT"/>
<int-jdbc:sql-parameter-definition name="get_ResultSet" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<!-- Parameter Mappings Before Passing & Receiving -->
<int-jdbc:parameter name="firstname" expression="payload[0]"/>
<int-jdbc:parameter name="lastname" expression="payload[1]"/>
<int-jdbc:returning-resultset name="get_ResultSet" row-mapper="com.support.PersonalinfoMapper"/>
</int-jdbc:stored-proc-outbound-gateway>
And in Mapper class I had the following simple Mapping rule:
...
PersonalInfo personalInfo = new PersonalInfo();
try{
personalInfo.setFirstname(resultSet.getString(DBConstants.FIRSTNAME));
personalInfo.setLastname(resultSet.getString(DBConstants.LASTNAME));
...
I did that because in my Model class i was having getter and setter for all CURSOR return elements.
Now, for one return variable (with '1' or Error Message) along with n-number of CURSORS:
I changed my Gateway definition as follows:
<!-- Stored Procedure Outbound-Gateway = To call a database stored procedure -->
<int-jdbc:stored-proc-outbound-gateway id="outbound-gateway-storedproc-personalinfo"
request-channel="procedureRequestChannel"
data-source="dataSource"
stored-procedure-name="pkg_personalinfo_spring.proc_personalinfo_spring"
expect-single-result="false"
ignore-column-meta-data="true">
<!-- Parameter Definitions -->
<int-jdbc:sql-parameter-definition name="firstname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="lastname" direction="IN"/>
<int-jdbc:sql-parameter-definition name="p_RetVal" direction="OUT"/>
<int-jdbc:sql-parameter-definition name="get_curr_1" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<int-jdbc:sql-parameter-definition name="get_curr_2" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<!-- Parameter Mappings Before Passing & Receiving -->
<int-jdbc:parameter name="firstname" expression="payload[0]"/>
<int-jdbc:parameter name="lastname" expression="payload[1]"/>
</int-jdbc:stored-proc-outbound-gateway>
And secondly I removed the entire row mapping concept, means I didn't have any Mapper class. The only one thing I had was only one Model class with all the CURSORS element name and their getters and setters.
Also notice that in my 'Parameter Definitions', how added those two CURSORS definition but in 'Parameter Mappings Before Passing & Receiving' I have nothing now.
I ran the App without any exceptions and later through Fiddler observed the following JSON data came with RESPOND, means everything PERFECT! ;)
JSON
- {}
- get_curr_1
- -{}
- - firstname=Faisal
- - lastname=Quazi
- get_curr_2
- -{}
- - country=Bangladesh
- - capital=Dhaka
WHAT THE HACK??? Yes...I had the same feelings ;)
Now, can any one plz help to understand what is going on here. Will it be a true statement that "'int-jdbc:stored-proc-outbound-gateway' is able to MAP ur Model element(s) by itself magically???"
Is there any best practice to deal with such kind of situation(s)?
Thank u guys always :)
:-). I guess that by default Spring JDBC uses ColumnMapRowMapper.
So you end up after that gateway with Map<String, <Map<?, ?>>> or something similar. But it is Map anyway.
Since you further convert your payload to JSON and Jackson can get deal with maps prefectly, you finish with correct result.
I can debug it, of course, but deduction says that it can't be differently.
UPDATE
As I said. The answer is here JdbcTemplate#extractOutputParameters:
....
if (outParam.isResultSetSupported()) {
returnedResults.putAll(processResultSet((ResultSet) out, outParam));
}
else {
String rsName = outParam.getName();
SqlReturnResultSet rsParam = new SqlReturnResultSet(rsName, new ColumnMapRowMapper());
returnedResults.putAll(processResultSet((ResultSet) out, rsParam));
if (logger.isDebugEnabled()) {
logger.debug("Added default SqlReturnResultSet parameter named '" + rsName + "'");
}
}
....

Need spring jdbc resultset to return list of objects instead of LinkedHashMap

I am using with a row mapper expecting it wuold return me the list of my objects but instead it is returning me LinkedHasMap. I want resultset to be List of my mapped objects.
Any idea how can i achieve this ?
Config
<int-jdbc:stored-proc-inbound-channel-adapter
channel="eventObj"
stored-procedure-name="p_get_completed_data"
data-source="dataSource"
auto-startup="true"
id="completedEventAdpt"
ignore-column-meta-data="true"
is-function="false"
return-value-required="false">
<int:poller max-messages-per-poll="1" fixed-rate="180000"/>
<int-jdbc:returning-resultset name="mapper" row-mapper="mapper" />
</int-jdbc:stored-proc-inbound-channel-adapter>
<bean id="mapper" class="com.db.mapper.MyMapper"/>
Stored procedures can return multiple result sets, keyed by the parameter name.
In your case, you only have one result set, but it's still returned in a map.
Simply add a <transformer/> after the inbound adapter...
<transformer ... expression="payload['mapper']" />

Resources