Sending-receiving collection over RabbitMQ in Spring Boot - spring-boot

Assume the config, applied both on sending and receiving sides:
#Configuration
#EnableRabbit
public class EventsConfig {
#Bean
public Jackson2JsonMessageConverter messageConverter(ObjectMapper objectMapper) {
return new Jackson2JsonMessageConverter(objectMapper);
}
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, Jackson2JsonMessageConverter jackson2JsonMessageConverter) {
final var rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
return rabbitTemplate;
}
}
Assume also, that there is a simple Spring Boot RabbitMQ listener on receiving side, declared like:
#RabbitListener(queues = "${amqp.inbox}")
public void listen(org.springframework.amqp.core.Message message) {
...
}
Attempt to receive a collection of List<TaskAssignment> (where TaskAssignment is a simple POJO having two UUIDs), sent by rabbit template, is ended on receiver side with exception:
accounting-service_1 | 01:26:39.610 WARN [org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler$DefaultExceptionStrategy] - Fatal message conversion error; message rejected; it will be dropped or routed to a dead letter exchange, if so configured: (Body:'[{"assigneeId":"1033c1ab-f277-4162-a711-722966bf69ec","taskId":"ae408e2d-39e0-4094-bf52-8c171d965be7"}]' MessageProperties [headers={__ContentTypeId__=java.lang.Object, __TypeId__=io.vavr.collection.List$Cons}, messageId=83f6bb30-a2b8-434a-867e-08684241e9b0, type=TASKS_REASSIGNED, contentType=application/json, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, redelivered=false, receivedExchange=outbox, receivedRoutingKey=, deliveryTag=3, consumerTag=amq.ctag-PodhhDJx6JtYsjIg6Cof1Q, consumerQueue=accounting-service])
accounting-service_1 | 01:26:39.610 ERROR [org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer] - Execution of Rabbit message listener failed, and the error handler threw an exception
accounting-service_1 | org.springframework.amqp.AmqpRejectAndDontRequeueException: Error Handler converted exception to fatal
accounting-service_1 | at org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler.handleError(ConditionalRejectingErrorHandler.java:146)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeErrorHandler(AbstractMessageListenerContainer.java:1460)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.handleListenerException(AbstractMessageListenerContainer.java:1744)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1519)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:968)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:914)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1289)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1195)
accounting-service_1 | at java.base/java.lang.Thread.run(Unknown Source)
accounting-service_1 | Caused by: org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Failed to convert message
accounting-service_1 | at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:146)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1656)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1575)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1563)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1554)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1498)
accounting-service_1 | ... 6 common frames omitted
accounting-service_1 | Caused by: org.springframework.amqp.support.converter.MessageConversionException: Failed to convert Message content
accounting-service_1 | at org.springframework.amqp.support.converter.AbstractJackson2MessageConverter.doFromMessage(AbstractJackson2MessageConverter.java:294)
accounting-service_1 | at org.springframework.amqp.support.converter.AbstractJackson2MessageConverter.fromMessage(AbstractJackson2MessageConverter.java:271)
accounting-service_1 | at org.springframework.amqp.support.converter.AbstractJackson2MessageConverter.fromMessage(AbstractJackson2MessageConverter.java:251)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:344)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:347)
accounting-service_1 | at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:132)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:229)
accounting-service_1 | at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:136)
accounting-service_1 | ... 11 common frames omitted
accounting-service_1 | Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `byte` from Object value (token `JsonToken.START_OBJECT`)
accounting-service_1 | at [Source: (String)"[{"assigneeId":"1033c1ab-f277-4162-a711-722966bf69ec","taskId":"ae408e2d-39e0-4094-bf52-8c171d965be7"}]"; line: 1, column: 2] (through reference chain: byte[][0])
accounting-service_1 | at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
Vavr module for Jackson is installed in ObjectMapper, so the root cause is not the fact that the collection is wrong and unserializable.
Notable thing about the stack trace is the headers part:
headers={__ContentTypeId__=java.lang.Object, __TypeId__=io.vavr.collection.List$Cons}
My theory is: since listener has received a JSON array, and Jackson requires explicit TypeReference to be provided in order to deduce a POJO, to which to deserialize array's items, that was the reason failure occurred in Jackson2MessageConverter. Nobody have given it the contents type.
The question is - how to provide Jackson2MessageConverter with such info? RabbitTemplate doesn't seem to give any means. Explicit setup of __ContentTypeId__ on sender side doesn't help either.
Or how to at least overcome Jackson deserialization on receiving side and listen to just raw messages in #RabbitListener with byte[] payload?
UPD: made a project reproducing the issue here: https://www.dropbox.com/s/rde9u02mxob189w/proba_amqp.zip?dl=0

You are attempting to receive the raw, unconverted Message.
Try listen(List<Foo> foos) instead so we can pass the inferred type to the converter.
EDIT
The issue is that your collection object is not compatible with Jackson.
This works
#PostConstruct
public void init() {
List<POJO> payload = new ArrayList<>();
payload.add(new POJO(1,2));
payload.add(new POJO(3,4));
//POJO payload = new POJO(1,2);
template.convertAndSend(
outbox,
"",
payload,
message -> {
var props = message.getMessageProperties();
props.setMessageId(UUID.randomUUID().toString());
return message;
}
);
}
#RabbitListener(queues = "${amqp.inbox}")
public void listen(List<POJO> pojos) {
System.out.println("Message reached:" + pojos);
}
Message reached:[com.skapral.POJO#7af8fa38, com.skapral.POJO#5569d76c]
FROM AUTHOR:
This answer gave me a hint towards the root cause. It turns out there is the bug in vavr-jackson - Jackson module for supporting Vavr collections.
https://github.com/vavr-io/vavr-jackson/issues/189

See if this sample helps you somehow:
https://github.com/spring-projects/spring-amqp-samples/tree/main/spring-rabbit-json
The docs is here: https://docs.spring.io/spring-amqp/docs/current/reference/html/#json-message-converter
UPDATE
See what would be a difference if you use this option on the converter:
/**
* When false (default), fall back to type id headers if the type (or contents of a container
* type) is abstract. Set to true if conversion should always be attempted - perhaps because
* a custom deserializer has been configured on the {#link ObjectMapper}. If the attempt fails,
* fall back to headers.
* #param alwaysAttemptConversion true to attempt.
* #since 2.2.8
*/
public void setAlwaysConvertToInferredType(boolean alwaysAttemptConversion) {

Related

Kafka transaction rollback not working with 3 topics for RecordTooLargeException

I post 3 message to 3 topics - while posting if gets exception - all messages will be rolled back.
But in my case it is not happening when I simulate the below exception for 3rd Topic.
org.apache.kafka.common.errors.RecordTooLargeException: The message is 117440606 bytes
while posting large message to the 3rd topic (price topic)- I programmatically increase the Size of the message to get exception.
Message are send to 1st 2 topic successfully - 3rd one failed. - As per transaction all messages must be rolled back - but topic 1 and 2 all the time gets the message.
But LOG shows - Transaction rolled back
HOW to FIX this issue
Log
2022-03-23 21:16:59.690 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO c.a.m.r.producer.KafkaProducer - ### --- Sending Data to Item , price, Inventory -----
2022-03-23 21:16:59.733 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] ERROR o.s.k.s.LoggingProducerListener - Exception thrown when sending a message with key='String' and payload='{"sku":"String","lowestOriginalPrice":...' to topic PRICE-TOPIC:
**org.apache.kafka.common.errors.RecordTooLargeException**: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
2022-03-23 21:16:59.733 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.producer.KafkaProducer - [Producer clientId=raw-item-producer-client-2, transactionalId=tx-5b01ad71-f754-44e0-9c52-4774a482bc1d0] Aborting incomplete transaction
2022-03-23 21:16:59.737 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.producer.KafkaProducer - [Producer clientId=raw-item-producer-client-1, transactionalId=tx-5b01ad71-f754-44e0-9c52-4774a482bc1draw-item-processor-group-id.OSMI_C02_CATALOG_MKPDOMAIN.0] **Aborting incomplete transaction**
2022-03-23 21:16:59.738 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] ERROR o.s.k.l.KafkaMessageListenerContainer - Transaction rolled back
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener method 'public void com.albertsons.mkp.rawitemprocessor.consumer.KafkaConsumer.receive(org.apache.kafka.clients.consumer.ConsumerRecord<java.lang.String, java.lang.String>) throws java.io.IOException' threw exception; nested exception is org.springframework.kafka.KafkaException: Send failed; nested exception is org.apache.kafka.common.errors.RecordTooLargeException: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.; nested exception is org.springframework.kafka.KafkaException: Send failed; nested exception is org.apache.kafka.common.errors.RecordTooLargeException: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
2022-03-23 21:17:00.250 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO c.a.m.r.producer.KafkaProducer - ### --- Sending Data to Item , price, Inventory -----
2022-03-23 21:17:00.294 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] ERROR o.s.k.s.LoggingProducerListener - Exception thrown when sending a message with key='String' and payload='{"sku":"String","lowestOriginalPrice":"String","lowestPrice":"String","updatedAt":"String","createdA...' to topic PRICE-TOPIC:
org.apache.kafka.common.errors.RecordTooLargeException: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
2022-03-23 21:17:00.295 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.producer.KafkaProducer - [Producer clientId=raw-item-producer-client-2, transactionalId=tx-5b01ad71-f754-44e0-9c52-4774a482bc1d0] Aborting incomplete transaction
2022-03-23 21:17:00.298 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO o.a.k.c.producer.KafkaProducer - [Producer clientId=raw-item-producer-client-1, transactionalId=tx-5b01ad71-f754-44e0-9c52-4774a482bc1draw-item-processor-group-id.OSMI_C02_CATALOG_MKPDOMAIN.0] **Aborting incomplete transaction**
2022-03-23 21:17:00.308 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] ERROR o.s.k.l.**KafkaMessageListenerContainer - Transaction rolled back**
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener method 'public void com.albertsons.mkp.rawitemprocessor.consumer.KafkaConsumer.receive(org.apache.kafka.clients.consumer.ConsumerRecord<java.lang.String, java.lang.String>) throws java.io.IOException' threw exception; nested exception is **org.springframework.kafka.KafkaException: Send failed**; nested exception is org.apache.kafka.common.errors.**RecordTooLargeException**: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.; nested exception is org.springframework.kafka.KafkaException: Send failed; nested exception is org.apache.kafka.common.errors.RecordTooLargeException: The message is 117440606 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
Rolled back records remain in the log.
Kafka adds a marker to the log to indicate whether the transaction was committed or rolled back.
By default, consumers will receive all records, even if they are rolled back.
Consumers must be configured with isolation.level=read_committed to avoid seeing rolled back records.
https://kafka.apache.org/documentation/#consumerconfigs_isolation.level
Controls how to read messages written transactionally. If set to read_committed, consumer.poll() will only return transactional messages which have been committed. If set to read_uncommitted (the default), consumer.poll() will return all messages, even transactional messages which have been aborted. Non-transactional messages will be returned unconditionally in either mode.
Messages will always be returned in offset order. Hence, in read_committed mode, consumer.poll() will only return messages up to the last stable offset (LSO), which is the one less than the offset of the first open transaction. In particular any messages appearing after messages belonging to ongoing transactions will be withheld until the relevant transaction has been completed. As a result, read_committed consumers will not be able to read up to the high watermark when there are in flight transactions.
When using Spring Boot, it's read-committed, not read_committed.
spring.kafka.consumer.isolation-level=read-committed
Your IDE should suggest proper values.
Or
spring.kafka.consumer.properties=isolation.level=read_committed
EDIT
(Although I see that Boot works with read_uncommitted too).
This works as expected for me.
#SpringBootApplication
public class So71591355Application {
public static void main(String[] args) {
SpringApplication.run(So71591355Application.class, args);
}
#KafkaListener(id = "so71591355", topics = "so71591355")
void listen1(String in) {
System.out.println("committed: " + in);
}
#KafkaListener(id = "so71591355-2", topics = "so71591355",
properties = "isolation.level:read_uncommitted")
void listen2(String in) {
System.out.println("uncommitted: " + in);
}
#Bean
public NewTopic topic() {
return TopicBuilder.name("so71591355").partitions(1).replicas(1).build();
}
#Bean
ApplicationRunner runner(KafkaTemplate<String, String> template) {
template.setAllowNonTransactional(true);
return args -> {
template.send("so71591355", "non-transactional");
try {
template.executeInTransaction(t -> {
t.send("so71591355", "first");
t.send("so71591355", "second");
t.send("so71591355", new String(new byte[2000000]));
return null;
});
}
catch (Exception e) {
e.printStackTrace();
}
};
}
}
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.isolation-level=read-committed
spring.kafka.producer.transaction-id-prefix=tx-
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.4)
org.springframework.kafka.KafkaException: Send failed; nested exception is org.apache.kafka.common.errors.RecordTooLargeException: The message is 2000088 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
at org.springframework.kafka.core.KafkaTemplate.doSend(KafkaTemplate.java:660)
at org.springframework.kafka.core.KafkaTemplate.send(KafkaTemplate.java:403)
at com.example.demo.So71591355Application.lambda$1(So71591355Application.java:49)
at org.springframework.kafka.core.KafkaTemplate.executeInTransaction(KafkaTemplate.java:507)
at com.example.demo.So71591355Application.lambda$0(So71591355Application.java:44)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768)
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:310)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301)
at com.example.demo.So71591355Application.main(So71591355Application.java:19)
Caused by: org.apache.kafka.common.errors.RecordTooLargeException: The message is 2000088 bytes when serialized which is larger than 1048576, which is the value of the max.request.size configuration.
uncommitted: non-transactional
committed: non-transactional
uncommitted: first
uncommitted: second
EDIT2
Your application is working as expected; when I add
#KafkaListener(id = "otherApp", topics = { "ITEM-TOPIC", "INVENTORY-TOPIC", "PRICE-TOPIC" })
void listen3(String in, #Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
System.out.println("so71591355 from " + topic + ": " + in);
}
to another application, it receives no data.
2022-03-24 10:04:57.939 INFO 15038 --- [ hisApp-0-C-1] o.s.k.l.KafkaMessageListenerContainer : otherApp: partitions assigned: [PRICE-TOPIC-0, ITEM-TOPIC-0, INVENTORY-TOPIC-0]
Of course, with a console consumer, we see the messages because the console consumer is not read_committed.
And when I comment out the price send; I see
so71591355 from INVENTORY-TOPIC: Inventory data : My test Message
so71591355 from ITEM-TOPIC: Item data : My test Message
...
EDIT3
To customize the after rollback processor; simply add it as a #Bean and Boot will wire it into the container factory.
#Bean
AfterRollbackProcessor<Object, Object> arp() {
return new DefaultAfterRollbackProcessor<>((rec, ex) -> {
log.error("Failed to process {} from topic, partition {}-{}, #{}",
rec.value(), rec.topic(), rec.partition(), rec.offset(), ex);
}, new FixedBackOff(3000L, 2));
}
However, you should remove the excuteInTransaction call and just do the sends directly on the template. That way, the template will participate in the container's transaction instead of starting a new one.
This example just logs the error; you can add DeadLetterPublishingRecoverer (or any custom recoverer).

SpelEvaluationException when initiating GET on a SftpOutboundGateway

I have a Spring Integration Flow starting with a SFTPOutboundGateway. It should get a file from a SFTP server. The file contains Event objects in JSON format. My configuration is:
#Bean
public DirectChannelSpec sftpGetInputChannel() {
return MessageChannels.direct();
}
#Bean
public QueueChannelSpec remoteFileOutputChannel() {
return MessageChannels.queue();
}
#Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(5000));
return pollerMetadata;
}
#Bean
public IntegrationFlow sftpGetFlow(TransferContext context) {
return IntegrationFlows.from("sftpGetInputChannel")
.handle(Sftp.outboundGateway(sftpSessionFactory(context.getChannel()),
AbstractRemoteFileOutboundGateway.Command.GET, "payload")
.remoteDirectoryExpression(context.getRemoteDir())
.localDirectory(new File(context.getLocalDir()))
.localFilenameExpression(context.getLocalFilename())
)
.channel("remoteFileOutputChannel")
.transform(Transformers.fromJson(Event[].class))
.get();
}
To get a file from the SFTP server I send a message to the gateway's input channel "sftpGetInputChannel":
boolean sent = sftpGetInputChannel.send(new GenericMessage<>(env.getRemoteDir() + "/" + env.getRemoteFilename()));
An SpelEvaluationException is thrown when trying to interprete the given filename. Both fields, remoteDir and remoteFilename are of type String:
org.springframework.messaging.MessageHandlingException: error occurred in message handler [bean 'sftpGetFlow.sftp:outbound-gateway#0' for component 'sftpGetFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [com/harry/potter/job/config/SftpConfiguration.class]'; from source: 'bean method sftpGetFlow']; nested exception is org.springframework.messaging.MessagingException: Failed to execute on session; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.integration.support.utils.IntegrationUtils.wrapInHandlingExceptionIfNecessary(IntegrationUtils.java:192)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:79)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:570)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:520)
at com.harry.potter.job.MyTask.getFile(MyEventsTask.java:170)
at com.harry.potter.job.myTask.runAsTask(MyEventsTask.java:112)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:836)
Caused by: org.springframework.messaging.MessagingException: Failed to execute on session; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:448)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.doGet(AbstractRemoteFileOutboundGateway.java:680)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.handleRequestMessage(AbstractRemoteFileOutboundGateway.java:584)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
... 21 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from org.springframework.messaging.support.GenericMessage<?> to java.lang.String
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:75)
at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:57)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:377)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.generateLocalFileName(AbstractRemoteFileOutboundGateway.java:1316)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.get(AbstractRemoteFileOutboundGateway.java:1081)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.lambda$doGet$6(AbstractRemoteFileOutboundGateway.java:681)
at org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway$$Lambda$30183/0x0000000000000000.doInSession(Unknown Source)
at org.springframework.integration.file.remote.RemoteFileTemplate.execute(RemoteFileTemplate.java:439)
... 25 common frames omitted
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.messaging.support.GenericMessage<?>] to type [java.lang.String]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:70)
... 32 common frames omitted
What do I wrong?
Okay, the problem is not within the message to get the file but in the configuration of the gateway:
.localFilenameExpression(context.getLocalFilename())
Just to set the local filename as expression is not allowed because dots - often part of the filename - is a SpEL delimiter to separate bean from method name. Hence the expression becomes invalid.
Possible expression would be:
.localFilenameExpression("#remoteFileName")
See its JavaDocs:
/**
* Specify a SpEL expression for local files renaming after downloading.
* #param localFilenameExpression the SpEL expression to use.
* #return the Spec.
*/
public S localFilenameExpression(String localFilenameExpression) {
And here is some code snippet how it works:
private String generateLocalFileName(Message<?> message, String remoteFileName) {
if (this.localFilenameGeneratorExpression != null) {
EvaluationContext evaluationContext = ExpressionUtils.createStandardEvaluationContext(getBeanFactory());
evaluationContext.setVariable("remoteFileName", remoteFileName);
return this.localFilenameGeneratorExpression.getValue(evaluationContext, message, String.class);
}
return remoteFileName;
}
The expression you can provide should somehow be in the context of the currently downloaded remote file. Not sure what is yours static context.getLocalFilename(). You can build a local file name based on the request message and that remoteFileName variable contexts.
See more in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/sftp.html#configuring-with-the-java-dsl-3
The sample over there is like this:
.localDirectoryExpression("'myDir/' + #remoteDirectory")
.localFilenameExpression("#remoteFileName.replaceFirst('sftpSource', 'localTarget')"))

getting method not found exception while consuming rabbitmq message

Have created on consumer class to consume message from rabbitMQ GUI.
but when i am publishing any message from rabbitMQ GUI its giving error class not found. and continuously error is printing on console.
may be problem is rabbitGUI sending message in from of byte, and i am trying to receive it on string.
I wanted to call the particular method based on message body.
#Component
#RabbitListener(queues = {"#{addNeRangeQueue.name}"})
public class consumerController {
#RabbitHandler
public void receive(String message) {
System.out.println("rabbit mq listener receive : "+message);
}
}
#Configuration
public class RabbitMQConsumerConfig {
//properties value configuration i have removed here.
#Bean
public Queue addNeRangeQueue() {
return new Queue(queueName,false);
}
#Bean
public TopicExchange getTopicEchangeName() {
return new TopicExchange(exchange);
}
#Bean
public Binding bindaddNeRange(TopicExchange getTopicEchangeName, Queue addNeRangeQueue) {
return BindingBuilder.bind(addNeRangeQueue).to(getTopicEchangeName).with(routingkey);
}
}
Exception:
2020-09-08 00:19:10.378 WARN 9456 --- [ntContainer#0-1] s.a.r.l.ConditionalRejectingErrorHandler : Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Listener threw exception
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:1705) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1595) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1510) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1498) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1489) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1433) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:970) [spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:916) [spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83) [spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1291) [spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1197) [spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_211]
Caused by: org.springframework.amqp.AmqpException: No method found for class [B
at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getHandlerForPayload(DelegatingInvocableHandler.java:151) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.DelegatingInvocableHandler.getMethodFor(DelegatingInvocableHandler.java:270) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.getMethodFor(HandlerAdapter.java:84) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandlerAndProcessResult(MessagingMessageListenerAdapter.java:146) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:133) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1591) ~[spring-rabbit-2.2.10.RELEASE.jar:2.2.10.RELEASE]
... 10 common frames omitted
When publishing from the UI, you have to add a content_type property so the message converter knows what to convert the byte[] message body to.
With method-level #RabbitListener, the converter can infer the type from the method parameter - this is not possible with the class level annotation because we need to do the conversion first to determine which method to call.
For a simple String with the default SimpleMessageConverter:
When using JSON (and the Jackson2JsonMessageConverter):

RabbitHandler to create consumer and retry on Fatal Exception in Spring for queue on listening to RabbitMQ

I am using Spring AMQP RabbitHandler and have written the following code:
#RabbitListener(queues = "#{testQueue.name}")
public class Tut4Receiver {
#RabbitHandler
public void receiveMessage(String message){
System.out.println("Message received "+message);
}
}
The Queue is defined like:-
#Bean
public Queue testQueue() {
return new AnonymousQueue();
}
I am using separate code to initialize the Connection Factory.
My question is if RabbitMQ is down for some time, it keeps on retrying to create a consumer but only if it receives a ConnectionRefused error. But suppose the user does not exist in RabbitMQ and there is a gap in which a new user will be created, then it receives a fatal error from RabbitMQ and it never retries due to which the result is auto delete queue would be created on RabbitMQ without any consumers.
Stack Trace:
SimpleMessageListenerContainer] [SimpleAsyncTaskExecutor-11] [|] [|||] Consumer received fatal exception on startup
org.springframework.amqp.rabbit.listener.exception.FatalListenerStartupException: Authentication failure
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:476)
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1280)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.amqp.AmqpAuthenticationException: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
at org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:65)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:309)
at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:547)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils$1.createConnection(ConnectionFactoryUtils.java:90)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.doGetTransactionalResourceHolder(ConnectionFactoryUtils.java:140)
at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.getTransactionalResourceHolder(ConnectionFactoryUtils.java:76)
at org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.start(BlockingQueueConsumer.java:472)
... 2 common frames omitted
Caused by: com.rabbitmq.client.AuthenticationFailureException: ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN. For details see the broker logfile.
at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:339)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:813)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:767)
at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:887)
at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:300)
SimpleMessageListenerContainer] [SimpleAsyncTaskExecutor-11] [|] [|||] Stopping container from aborted consumer
[|] [|||] Waiting for workers to finish.
[|] [|||] Successfully waited for workers to finish.
Any way to retry even on fatal exceptions like when the user does not exist?
Authentication failures are considered fatal by default and not retried.
You can override this behavior by setting a property on the listener container (possibleAuthenticationFailureFatal). The property is not available as a boot property so you have to override boot's container factory...
#Bean(name = "rabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setContainerConfigurer(smlc -> smlc.setPossibleAuthenticationFailureFatal(false));
return factory;
}

SpEL evaluation of path variable in WebsocketSecurityConfiguration

I am running a JHipster 6.1.2 Gateway with Websockets and am trying to restrict access to a messaging topic, so that users can only subscribe to topics of the institution they belong to. So basically I want to perform a check on the id from the subscription path.
My current solution is based on https://stackoverflow.com/a/44895369/4246074 and looks as follows:
WebsocketSecurityConfiguration.java:
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
//...
//User can only subscribe to own institution topic
.simpSubscribeDestMatchers("/topic/institution.{id}")
.access("#institutionIdGuard.checkInstitutionId(#id)")
//...
}
InstitutionIdGuard.java:
#Component
public class InstitutionIdGuard {
public boolean checkInstitutionId(Long institutionId) {
//validation logic for institutionId would go here
return true;
}
The problem:
Apparently the SpEL expression can't access {id} from the path because i get a nullpointer error with the following log:
2019-08-08 10:30:08.367 ERROR 31097 --- [ XNIO-1 I/O-1] o.s.w.s.m.StompSubProtocolHandler : Failed to send client message to application via MessageChannel in session j2a0jlos. Sending STOMP ERROR to client.
org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is java.lang.IllegalArgumentException: Failed to evaluate expression '#institutionIdGuard.checkInstitutionId(#id)'
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:146)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:122)
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:284)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:324)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.delegateMessages(AbstractSockJsSession.java:386)
at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.handleMessage(WebSocketServerSockJsSession.java:195)
at org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler.handleTextMessage(SockJsWebSocketHandler.java:93)
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:113)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:84)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at io.undertow.websockets.jsr.FrameHandler$7.run(FrameHandler.java:286)
at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:170)
at io.undertow.websockets.jsr.ServerWebSocketContainer$1.call(ServerWebSocketContainer.java:167)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:604)
at io.undertow.websockets.jsr.ServerWebSocketContainer.invokeEndpointMethod(ServerWebSocketContainer.java:594)
at io.undertow.websockets.jsr.FrameHandler.invokeTextHandler(FrameHandler.java:266)
at io.undertow.websockets.jsr.FrameHandler.onFullTextMessage(FrameHandler.java:317)
at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:156)
at io.undertow.websockets.core.AbstractReceiveListener$2.complete(AbstractReceiveListener.java:152)
at io.undertow.websockets.core.BufferedTextMessage.read(BufferedTextMessage.java:105)
at io.undertow.websockets.core.AbstractReceiveListener.readBufferedText(AbstractReceiveListener.java:152)
at io.undertow.websockets.core.AbstractReceiveListener.bufferFullMessage(AbstractReceiveListener.java:90)
at io.undertow.websockets.jsr.FrameHandler.onText(FrameHandler.java:182)
at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:44)
at io.undertow.websockets.core.AbstractReceiveListener.handleEvent(AbstractReceiveListener.java:33)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:951)
at io.undertow.server.protocol.framed.AbstractFramedChannel$FrameReadListener.handleEvent(AbstractFramedChannel.java:932)
at org.xnio.ChannelListeners.invokeChannelListener(ChannelListeners.java:92)
at org.xnio.conduits.ReadReadyHandler$ChannelListenerHandler.readReady(ReadReadyHandler.java:66)
at org.xnio.nio.NioSocketConduit.handleReady(NioSocketConduit.java:88)
at org.xnio.nio.WorkerThread.run(WorkerThread.java:561)
Caused by: java.lang.IllegalArgumentException: Failed to evaluate expression '#institutionIdGuard.checkInstitutionId(#id)'
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:57)
at org.springframework.security.messaging.access.expression.MessageExpressionVoter.vote(MessageExpressionVoter.java:39)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:63)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233)
at org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor.preSend(ChannelSecurityInterceptor.java:69)
at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:178)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:132)
... 37 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method checkInstitutionId(null) cannot be found on type com.mycompany.websocketgateway.security.InstitutionIdGuard
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134)
at org.springframework.expression.spel.ast.MethodReference.access$000(MethodReference.java:54)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:390)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:90)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:300)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:26)
... 44 common frames omitted
I would be grateful for any ideas how to make my solution work or other other ways of performing checks in the id.
I found the solution on the Spring Security issue tracker. Apparently before Spring Security 5.2 you can pass the implicit message variable to the SpEL expression
.simpSubscribeDestMatchers("/topic/institution.*")
.access("#institutionIdGuard.checkInstitutionId(authentication, message)")
Then in the verification method its possible to get the path from the message and do your own verification with it:
public boolean checkInstitutionId(Authentication authentication, Message<?> message) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
String topic = sha.getDestination();
String id = topic.replace("/topic/institution/", "");
//validation logic for institutionId would go here
return true;
}
Spring Security 5.2 should have fixed the issue according to this pull request.

Resources