I have declared a mail listener with Spring Integration like this:
#Bean
public IntegrationFlow mailListener() {
return IntegrationFlows.from(
Mail.imapIdleAdapter(getUrl())
.searchTermStrategy((s, f) -> new FlagTerm(new Flags(Flags.Flag.SEEN), false))
.shouldMarkMessagesAsRead(true)
.shouldDeleteMessages(false)
.get())
.<Message>handle((payload, header) -> handle(payload)).get();
}
In my test mail account I have a few 'unread' and a few 'read' messages. Starting the application I see in the logs that the listener fetches all of the 'unread' messages over and over again without ever marking them as 'read'.
Given that I specified shouldMarkMessagesAsRead(true) I would expect the Adapter to mark a message as read after fetching it.
Am I understanding and/or doing something wrong?
Thanks to Artem Bilan's hint on activating debug output I found out that the mailbox was opened in read-only mode.
And thanks to Gary Russell's answer to another question I tried removing the .get() call on the ImapIdleChannelAdapterSpec:
#Bean
public IntegrationFlow mailListener() {
return IntegrationFlows.from(
Mail.imapIdleAdapter(getUrl())
.shouldMarkMessagesAsRead(true)
.shouldDeleteMessages(false))
.<Message>handle((payload, header) -> handle(payload)).get();
}
Now the mailbox gets opened in read-write mode and marking the messages with the SEEN flag works fine.
I also don't actually need the custom SearchTermStrategy now as Artem Bilan already suggested.
In this type of situations we recommend to set:
.javaMailProperties(p -> p.put("mail.debug", "true"))
on that Mail.imapIdleAdapter().
Probably your e-mail server really does not support that \seen flag, so the message is marked as read via some other flag.
So, with that mail debugging option you should see some interesting info in your logs.
The logic in our default DefaultSearchTermStrategy is like this around that Flags.Flag.SEEN :
if (supportedFlags.contains(Flags.Flag.SEEN)) {
NotTerm notSeen = new NotTerm(new FlagTerm(new Flags(Flags.Flag.SEEN), true));
if (searchTerm == null) {
searchTerm = notSeen;
}
else {
searchTerm = new AndTerm(searchTerm, notSeen);
}
}
See if you really need a custom strategy and why a default one is not enough for you: https://docs.spring.io/spring-integration/docs/current/reference/html/mail.html#search-term
Related
I'm trying to set the dead letter address for a queue via the JMS management API. From reading the latest Artemis docs it appears that I should be able to do this using the QueueControl.setDeadLetterAddress(...) method. See https://activemq.apache.org/artemis/docs/latest/management.html and search for "setDeadLetterAddress".
It is my understanding that the parameters of these methods should be found in the Artemis QueueControl javadocs here:
https://activemq.apache.org/artemis/docs/javadocs/javadoc-latest/org/apache/activemq/artemis/api/core/management/QueueControl.html
However, that documentation does not have any mention of a setDeadLetterAddress method or what parameters is might accept.
Does the QueueControl.setDeadLetterAddress method still exist and can it be called from the JMSManagementHelper.putOperationInvocation(...) method?
Many thanks!
Looking at the QueueControlImpl class code it is clear that the setDeadLetterAddress operation is no longer present. An operation in the ActiveMQServerControlImpl class named addAddressSettings does provide the capability to set the DLA for a queue (as well as plenty of other settings).
For example:
Queue managementQueue = ActiveMQJMSClient.createQueue("activemq.management");
Queue replyQueue = ActiveMQJMSClient.createQueue("management.reply");
JMSContext context = connectionFactory.createContext();
JMSConsumer consumer = context.createConsumer(replyQueue)) {
JMSProducer producer = context.createProducer();
producer.setJMSReplyTo(replyQueue);
// Using AddressSettings isn't required, but is provided
// for clarity.
AddressSettings settings = new AddressSettings()
.setDeadLetterAddress(new SimpleString("my.messages.dla"))
.setMaxDeliveryAttempts(5)
.setExpiryAddress(new SimpleString("ExpiryAddress"))
.setExpiryDelay(-1L) // No expiry
.setLastValueQueue(false)
.setMaxSizeBytes(-1) // No max
.setPageSizeBytes(10485760)
.setPageCacheMaxSize(5)
.setRedeliveryDelay(500)
.setRedeliveryMultiplier(1.5)
.setMaxRedeliveryDelay(2000)
.setRedistributionDelay(1000)
.setSendToDLAOnNoRoute(true)
.setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE)
.setSlowConsumerThreshold(-1) // No slow consumer checking
.setSlowConsumerCheckPeriod(1000)
.setSlowConsumerPolicy(SlowConsumerPolicy.NOTIFY)
.setAutoCreateJmsQueues(true)
.setAutoDeleteJmsQueues(false)
.setAutoCreateJmsTopics(true)
.setAutoDeleteJmsTopics(false)
.setAutoCreateQueues(true)
.setAutoDeleteQueues(false)
.setAutoCreateAddresses(true)
.setAutoDeleteAddresses(false);
Message m = context.createMessage();
JMSManagementHelper.putOperationInvocation(m, ResourceNames.BROKER, "addAddressSettings",
"my.messages",
settings.getDeadLetterAddress().toString(),
settings.getExpiryAddress().toString(),
settings.getExpiryDelay(),
settings.isLastValueQueue(),
settings.getMaxDeliveryAttempts(),
settings.getMaxSizeBytes(),
settings.getPageSizeBytes(),
settings.getPageCacheMaxSize(),
settings.getRedeliveryDelay(),
settings.getRedeliveryMultiplier(),
settings.getMaxRedeliveryDelay(),
settings.getRedistributionDelay(),
settings.isSendToDLAOnNoRoute(),
settings.getAddressFullMessagePolicy().toString(),
settings.getSlowConsumerThreshold(),
settings.getSlowConsumerCheckPeriod(),
settings.getSlowConsumerPolicy().toString(),
settings.isAutoCreateJmsQueues(),
settings.isAutoDeleteJmsQueues(),
settings.isAutoCreateJmsTopics(),
settings.isAutoDeleteJmsTopics(),
settings.isAutoCreateQueues(),
settings.isAutoDeleteQueues(),
settings.isAutoCreateAddresses(),
settings.isAutoDeleteAddresses());
producer.send(managementQueue, m);
Message response = consumer.receive();
// addAddressSettings returns void but this will also return errors if the
// method or parameters are wrong.
log.info("addAddressSettings Reply: {}", JMSManagementHelper.getResult(response));
I have the following code:
Single<Response<User>> single = service.registerUser();
single
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(Response::body)
.flatMap(parentsRepsitory::writeUser)
.observeOn(AndroidSchedulers.mainThread())
.flatMap(parentsRepsitory::getUser)
Where the parentsRepository is a repo wraping my realm database. The problems come when the server returns validation errors, however. So somewhere in my stream i want to have the equivalent of
if(response.code() == 201){
// CONTINUE STREAM USING THE LOGIC THAT HANDLES SUCCESS
}elseif(response.code() == 400){
// CONTINUE STREAM USING LOGIC TO HANDLE THE VALIDATION ERRORS
}
A solution I have previously implemented is as follows:
Observable<Response<User>> observable_from_api =
service.attemptLogin(username, password)
.share();
observable_from_api
.filter(response -> response.code() == HttpStatus.HTTP_STATUS_200_OK)
.//handle logic for success
observable_from_api
.filter(response -> response.code() == HttpStatus.HTTP_STATUS_400_BAD_REQUEST)
.//handle logic for validation errors
I don't like this solution for several different reasons. The main one being it just does not seem right. The second one being that the .share() method is only available on an Observable object. Since my network operation emits only one responce I would much rather use Single instead, but the .share() method is not available there.
Excuse me if this is a duplicate question, I have done some digging around and only found the solution I mentioned. I want to either see the optimal solution or be told explicitly that this is in fact the optimal solution.
I think you need to define which kind of data you want your consumer to receive. I assume you want to receive in the consumer a User object.
These are the signatures of the method that you should create:
Single<User> handleSuccess(Response<User> response)
Single<User> handleError(Response<User> response)
And then you create you stream in this way:
service.registerUser()
.flatMap(response -> {
if (response.success) {
return handleSuccess(response);
} else {
return handleError(response);
}
})
.subscribe(user -> logd("user: " + user.name));
I wrote a trading app on the example Tradeclient that was on the quickfixn github page. It is now heavily modified, but regarding logging on and off it hasn't been changed. I now have the issue that when the logout button is pressed, it calls the Initiator.Stop, but it doesn't enter into OnLogout as it's supposed to do. When logging on it does everything right, initiator.start and then the OnCreate and then OnLogon, but when logging out, the OnLogout isn't triggered. Any ideas what could be the issue?
private void Disconnect(object ignored)
{
Trace.WriteLine("ConnectionViewModel::Disconnect called");
_qfapp.Stop();
}
public void Stop()
{
Trace.WriteLine("QFApp::Stop() called");
Initiator.Stop(true);
}
public void OnLogout(QuickFix.SessionID sessionID)
{
// not sure how ActiveSessionID could ever be null, but it happened.
string a = (this.ActiveSessionID == null) ? "null" : this.ActiveSessionID.ToString();
Trace.WriteLine(String.Format("==OnLogout: {0}==", a));
if (LogoutEvent != null)
{
LogoutEvent();
}
}
It's the same answer as DumbCoder here. The Initiator.Stop(true); stops the initiator engine so all sessions are disconnected. That means there are no more sessions to log out of.
Per this answer you want to first of all logout of the session(s) using
Session.lookupSession(sessionID).logout();
However, per Grant's comment there, that's an unusual thing to do. My guess is that onLogout() functionality is really to capture when client sessions log out of an acceptor, but the acceptor itself stays running. It's not really for the initiator side.
I have created an application for FIX transactions using QuickFIX c++ API. The application is connecting to the server but the server sends "Provide UserName<553>" message. I looked for and result and found that i need to add username and password to the toAdmin method created this following code to in order to achieve that
void Application::toAdmin( FIX::Message& message, const FIX::SessionID& sessionID)
{
if (FIX::MsgType_Logon == message.getHeader().getField(FIX::FIELD::MsgType))
{
FIX44::Logon& logon_message = dynamic_cast<FIX44::Logon&>(message);
logon_message.setField(FIX::Username("my_username"));
logon_message.setField(FIX::Password("my_password"));
}
}
But then it throws and Exception. Please suggest what to do
remove this line and exception is handled
FIX44::Logon& logon_message = dynamic_cast<FIX44::Logon&>(message);
after that Put it
message.setField(FIX::Username("my_username"));
```````
message.setField(FIX::Password("my_password"));
In developing some of my Web Applications I use debug("stuff here"); to help me figure out whats going on. I'd rather not have to go in and find all of my debug(*); lines and remove them. Is there a way I can specify at the application level not to display debug messages in a FeedBackPanel?
You could add an ErrorLevelFeedbackMessageFilter to your FeedbackPanels (there is a constructor accepting one). If you create the filter based on your deployment mode, this should be what you need.
In Detail:
Change your new FeedbackPanel("id") to new FeedbackPanel("id", Application.getFeedbackMessageFilter()) and implement this method as
public IFeedbackMessageFilter getFeedbackMessageFilter() {
IFeedbackMessageFilter filter = null;
if (RuntimeConfigurationType.DEVELOPMENT.equals(getConfigurationType())) {
filter = new ErrorLevelFeedbackMessageFilter(FeedbackMessage.DEBUG);
} else {
filter = new ErrorLevelFeedbackMessageFilter(FeedbackMessage.ERROR);
}
return filter;
}