how to pass parameter #Payload - spring-integration

i have code
<int:channel id="partnerConfigChannel" />
<int:gateway id="partnerService" service-interface="org.service.PartnerService"
default-request-timeout="5000" default-reply-timeout="5000">
<int:method name="findConfig" request-channel="partnerConfigChannel" />
</int:gateway>
<int-jpa:retrieving-outbound-gateway entity-manager="entityManager"
request-channel="partnerConfigChannel"
jpa-query="select q from QueueConfiguration q where q.partnerId = :partnerId">
<int-jpa:parameter name="partnerId" expression="payload['partnerId']" />
</int-jpa:retrieving-outbound-gateway>
and java interface
public interface PartnerService {
#Payload("partnerId")
List<QueueConfiguration> findConfig();
}
i am calling it
List<QueueConfiguration> qc= partnerService.findConfig();
but i am getting exception
EL1007E:(pos 0): Property or field 'partnerId' cannot be found on null
please tell me how can i pass payload . i tried by passing Message object with a map , string but same error .
please tell me how can i pass payload in such case.

#Payload("partnerId")
At this point, there is no object for the SpEL expression to be evaluated against.
It either needs to be a literal
#Payload("'partnerId'")
Or refer to some other bean.
Further, on your adapter, you are expecting the payload to be a map with key partnerId.
expression="payload['partnerId']"
So this won't work.
If you want to pass a variable, you should do something like this...
public interface PartnerService {
List<QueueConfiguration> findConfig(MyClass param);
Where MyClass has some property 'partnerId'.
or
List<QueueConfiguration> findConfig(String partnerId);
and
expression="payload"
I suggest you do some more reading.

i modified my code
public interface PartnerService {
List<QueueConfiguration> findConfig(#Payload Message msg);
}
and the call it like
Map msgMap=new HashMap();
msgMap.put("partnerId", partnerId);
Message msg=MessageBuilder.withPayload(msgMap).build();
List<QueueConfiguration> qc= partnerService.findConfig(msg);
and it is working fine.

Related

Shopware How to get SalesChannelContext in OrderWrittenEvent in order to send Mail?

I have a situation where I want to send emails from the order written event whenever an order has been updated according to some set of conditions that I will implement (for example an API response error) But unfortunately I have been unable to do so.
I first created a controller and an email service which uses the abstract email service of shopware And from my controller I'm able to send an email But when I tried to do the same in the event,I quickly realized that it wasn't doing exactly what I was expecting it to do. After some research on it, I saw that the event actually don't have access to the sales channel context so I tried multiple different ways to solve this issue but I'm still stuck. Can somebody please guide me on how I can implement that? thank you very much.
an example of what I tried is to call the store API in order to get the context of the saleschannel to use it in my sendMail function But it was giving errors such as:
request.CRITICAL: Uncaught PHP Exception TypeError: "Argument 5 passed to Swag\BasicExample\Service\EmailService::sendMail() must be an instance of Shopware\Core\System\SalesChannel\SalesChannelContext, instance of stdClass given.
I obviously understand that I have to give it a Shopware\Core\System\SalesChannel\SalesChannelContext not an STD class but how can I do that? since it doesn't really see the channel context.
If you do have an instance of OrderEntity you can rebuild the SalesChannelContext from the existing order using the OrderConverter service.
<service id="Foo\MyPlugin\Subscriber\MySubscriber">
<argument type="service" id="Shopware\Core\Checkout\Cart\Order\OrderConverter"/>
<argument type="service" id="order.repository"/>
<tag name="kernel.event_subscriber"/>
</service>
class MySubscriber implements EventSubscriberInterface
{
private OrderConverter $orderConverter;
private EntityRepository $repository;
public function __construct(
OrderConverter $orderConverter,
EntityRepository $repository
) {
$this->orderConverter = $orderConverter;
$this->repository = $repository;
}
public static function getSubscribedEvents(): array
{
return [
OrderEvents::ORDER_WRITTEN_EVENT => 'onOrderWritten',
];
}
public function onOrderWritten(EntityWrittenEvent $event): void
{
foreach ($event->getWriteResults() as $writeResult) {
$orderId = $writeResult->getPrimaryKey();
$criteria = new Criteria([$orderId]);
$criteria->addAssociation('transactions');
$criteria->addAssociation('orderCustomer');
$order = $this->repository
->search($criteria, $event->getContext())->first();
if (!$order instanceof OrderEntity) {
continue;
}
$salesChannelContext = $this->orderConverter
->assembleSalesChannelContext($order, $event->getContext());
// ...
}
}
}

mapping jms property to RabbitMQ

We use spring integration to communicate components using MQSeries and RabbitMQ.
With MQseries there are a lot of JMS properties that are used by customers.
We would like to add an intermediate function between the input and the output which makes a mapping of these properties (getting the jms_priority from mqseries and put in the output message the property "priority" for RabbitMQ).
Without the specific properties JMS it works very well.
Below the code we use:
<int:channel id="input" ></int:channel>
<int-jms:message-driven-channel-adapter id="jmsIn" connection-factory="connectionFactoryCaching" channel="input" ...>
<int:service-activator id="sa1" input-channel="input" ref="serviceBean" output-channel="output"/>
<bean id="serviceBean" class="com.poc.ServiceActivator"> </bean>
<int:channel id="output" ></int:channel>
<int-amqp:outbound-channel-adapter channel="output" .../>
import org.springframework.amqp.core.MessageProperties;
The ServiceActivator code:
public class ServiceActivator {
public org.springframework.amqp.core.Message convertMessageMQSeriesToRabbit (Message obj){
MessageProperties messageProperties = new MessageProperties();
try {
messageProperties.setCorrelationId(obj.getJMSCorrelationID());
System.out.println("getJMSReplyTo "+obj.getJMSReplyTo());
System.out.println("getJMSPriority "+obj.getJMSPriority());
messageProperties.setPriority(obj.getJMSPriority());
System.out.println("getJMSType "+obj.getJMSType());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Is this the right way to do it? In the convertMessageMQSeriesToRabbit method, what type of return object should be passed to the outbound? org.springframework.amqp.core.Message ?
In this method we want to fill all RabbitMQmessage properties (content_type, content_encoding, priority, correlation_id, reply_to, expiration, message_id, timestamp, type, user_id, app_id, cluster_id) with corresponding JMS value.
We also need to do it the other way (inboud rabbitMQ => outbound mqseries)
No, Spring Integration uses its own abstraction org.springframework.messaging.Message<?>. You don't need to interact with it directly, simply use a header-enricher.
See JmsHeaders and AmqpHeaders for the constants.
<header-enricher id="headerEnricherWithShouldSkipNullsFalse" input-channel="fromJms" output-channel="toRabbit">
<header name="amqp_correlationId" expression="headers.jms_correlationId"/>
...
</header-enricher>
For AMQP priority, the standard IntegrationMessageHeaderAccessor.PRIORITY value is used.
Also see the DefaultAmqpHeaderMapper and DefaultJmsHeaderMapper to see how the adapters map from Message<?> to/from RabbitMQ and JMS messageds.

Spring Integration - Use SpEL in service-activator nested bean constructor-arg

I am trying to use a factory-method to initialize a service-activator as below
<int:service-activator>
<bean class="com.sample.FileWriterFactory" factory-method="createFileWriter">
<constructor-arg index="0" value="${xml.out-directory}/xml"/>
<constructor-arg index="1" value="#{ headers['file_name'] + '.xml' }"/>
</bean>
</int:service-activator>
However, the SpEL evaluation fails since the headers property is not found in the evaluation context. The exact error snippet is
org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'headers' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
The purpose of doing this is that I want to reuse the same POJO by passing different parameters as needed.
What am I doing wrong?
#{...} expressions are evaluated once, during context initialization.
Expressions accessing properties (such as headers) like that need to be evaluated at runtime (against the message as the root object).
If that's what you are trying to do, use value="headers['file_name'] + '.xml'" and then, within your constructor...
private final Expression expression;
public FileWriterFactory(String directory, String expression) {
...
this.expression = new SpelExpressionParser().parseExpression(expression);
}
then, in your runtime service method
String fileName = this.expression.getValue(message, String.class);

File Poller FileListFilter cast exception

Doing exploratory work before I drink the kool-aid.
I am trying to create a simple inbound channel adapter to monitor a directory for new ZIP files.
In order to deal with the ever-present "is it complete?" question, I am trying to adapt the example posted here to incorporate a FileListFilter which checks the modified time of the file.
However, I am getting the following exception:
a boolean result is requiredclass java.util.ArrayList is not assignable to class java.lang.Boolean
at org.springframework.util.Assert.isAssignable(Assert.java:368)
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:61)
at org.springframework.integration.filter.MessageFilter.handleRequestMessage(MessageFilter.java:103)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
I had this working well with a simple router based on the file extension, but when I replaced it with this filter it fell apart. Seems the actual list of files is what the Assert is trying to cast to Boolean.
Is it not possible to have a filter wired between an inbound and an outbound adapter? Or must I do the file move to the destination myself in the filter? (The way it is done in the linked example)
Here is the config:
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="filesChannel" filename-pattern="*.zip">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
<int:filter input-channel="filesChannel" ref="lastModifiedFileFilter" output-channel="zipFilesOut"/>
<bean id="lastModifiedFileFilter" class="FileFilterOnLastModifiedTime">
<property name="timeDifference" value="10000"/>
</bean>
<int-file:outbound-channel-adapter id="zipFilesOut" directory="file:target/output/zip" delete-source-files="true" />
Here is the filter:
import java.io.File;
import org.springframework.integration.file.filters.AbstractFileListFilter;
public class FileFilterOnLastModifiedTime extends AbstractFileListFilter<File> {
Long timeDifference = 1000L;
#Override
protected boolean accept(File file) {
long lastModified = file.lastModified();
long currentTime = System.currentTimeMillis();
return (currentTime - lastModified) > timeDifference ;
}
public void setTimeDifference(Long timeDifference) {
this.timeDifference = timeDifference;
}
}
Your FileFilterOnLastModifiedTime bean should be provided to the inbound adapter using the filter attribute.
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="zipFilesOut" filename-pattern="*.zip"
filter="lastModifiedFileFilter">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
An inline <filter/> element is a simple POJO that takes some argument and returns a boolean.
Since you are providing an AbstractFileListFilter, the framework is trying to invoke filterFiles which takes an array and returns a List, not a boolean.

Passing 2/n-number of (multiple) parameters to 'int-jdbc:stored-proc-outbound-gateway'

Hi guys i need help here :)
Previously when I tried to pass only one variable, it was working fine. Now as soon as I am trying to pass 2/n-number of parameters by using Spring Integration, getting following 'payload' exceptions, which I am not clear about.
Exception I am getting is as follows:
[2014-10-31 12:12:43,943][WARN ]GatewayProxyFactoryBean$MethodInvocationGateway.doSendAndReceive: failure occurred in gateway sendAndReceive
org.springframework.messaging.converter.MessageConversionException: failed to convert object to Message
at org.springframework.integration.support.converter.SimpleMessageConverter.toMessage(SimpleMessageConverter.java:85)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:112)
at org.springframework.messaging.core.AbstractMessagingTemplate.convertSendAndReceive(AbstractMessagingTemplate.java:103)
...
Caused by: org.springframework.messaging.MessagingException: At most one parameter (or expression via method-level #Payload) may be mapped to the payload or Message. Found more than one on method [public abstract java.util.List com.dao.PersonalinfoDao.queryExecute(java.lang.String,java.lang.String)]
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.throwExceptionForMultipleMessageOrPayloadParameters(GatewayMethodInboundMessageMapper.java:235)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper.access$400(GatewayMethodInboundMessageMapper.java:77)
at org.springframework.integration.gateway.GatewayMethodInboundMessageMapper$DefaultMethodArgsMessageMapper.toMessage(GatewayMethodInboundMessageMapper.java:337)
...
Bellow showing what I am doing for passing 2 parameters:
In my PersonalinfoDao.java file I have this:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
In my PersonalinfoService.java file I have this:
public class PersonalinfoService {
#Autowired
private PersonalinfoDao personalinfoDao;
public List<PersonalInfo> dbConnect(String firstname, String lastname) {
List<PersonalInfo> personalinfoList = personalinfoDao.queryExecute(firstname, lastname);
return personalinfoList;
}
}
In Gateway definition file I have the following:
<!-- Mapper Declarations -->
<bean id="personalinfoMapper" class="com.support.PersonalinfoMapper"/>
<!-- Service Inheritance -->
<bean id="personalinfoService" class="com.service.PersonalinfoService"/>
<!-- Channels = For calling DAO interface methods in Spring Integration Mechanism one has to create request & response channels -->
<int:channel id="procedureRequestChannel"/>
<!-- Gateway = DAO Interface Method Mapped to Request & Response Channels -->
<int:gateway id="gateway_personalinfo" default-request-timeout="5000"
default-reply-timeout="5000"
service-interface="com.dao.PersonalinfoDao">
<int:method name="queryExecute" request-channel="procedureRequestChannel" />
</int:gateway>
<!-- 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="true"
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="get_ResultSet" type="#{T(oracle.jdbc.OracleTypes).CURSOR}" direction="OUT"/>
<!-- Parameter Mappings Before Passing & Receiving -->
<int-jdbc:parameter name="firstname" expression="payload"/>
<int-jdbc:parameter name="lastname" expression="payload"/>
<int-jdbc:returning-resultset name="get_ResultSet" row-mapper="com.support.PersonalinfoMapper"/>
</int-jdbc:stored-proc-outbound-gateway>
I know that i am doing something wrong in this above gateway definition specially when using expression="payload"...because for any given Getway i can use only one payload. But as I am not clear how to do that by using Map/Array/List, can any one plz help me to resolve this?
Thank u so much :)
Probably the simplest way is to use a #Payload annotation:
public interface PersonalinfoDao {
/** Method to call a SQL query using spring integration mechanism */
#Payload("#args")
public List<PersonalInfo> queryExecute(String firstname, String lastname);
}
Or use a payload-expression="#args" in the XML declaration of the <gateway/>'s method.
The framework will then make the payload an Object[] and you can use payload[0], payload[1] etc in your expressions.

Resources