Spring Integration Mail IMAP - multiple receiver - spring

I'm using spring boot 2.2.4.RELEASE
I need to build a dynamic mail receiver because I can have several mail server where to fetch mails. The mail server must be configurable by other systems so my requirement is to be dynamically able in fetching messages.
I investigated and I liked the Spring Integration solution and its DSL (note: it's enough to me to simply download messages and their attachments, if any).
So I built this code:
String flowId = MAIL_IN_FLOW_ID_PREFIX+cpd.getIndirizzoMail();
if( flowContext.getRegistrationById(flowId) != null ) {
flowContext.remove(flowId);
}
ImapMailInboundChannelAdapterSpec adapterSpec = Mail.imapInboundAdapter(connectionUrl.toString())
.javaMailProperties(javaMailProperties)
.shouldDeleteMessages(false)
.shouldMarkMessagesAsRead(false)
.selector(selectFunction);
if( confMailIn.isRichiedeAutenticazione() ) {
adapterSpec = adapterSpec.javaMailAuthenticator(new CasellaPostaleAuthenticator(cpd.getUsername(), cpd.getPassword()));
}
IntegrationFlow flow = IntegrationFlows
.from(adapterSpec.get(), e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(pollingSeconds)).maxMessagesPerPoll(maxMailMessagePerPoll)))
.handle(message -> {
logger.info("Message headers "+message.getHeaders());
logger.info("Message payload "+message.getPayload());
})
.get();
flowContext.registration(flow).id(flowId).register();
I tried by using my gmail account. The code is able in connecting to GMAIL by imap but, when I try to simply log messages, I get this error:
A6 OK Success
2020-02-05 12:48:41,835 23412 [task-scheduler-1] DEBUG
o.s.i.mail.ImapMailReceiver - Received 10 messages
2020-02-05 12:48:41,836 23413 [task-scheduler-1] DEBUG oA7 STORE 1
+FLAGS (\Flagged) .s.i.mail.ImapMailReceiver - USER flags are not supported by this mail server. Flagging message with system flag
A7 NO STORE attempt on READ-ONLY folder (Failure)
A8 CLOSE
A8 OK Returned to authenticated state. (Success)
DEBUG IMAP: added an Authenticated connection -- size: 1
2020-02-05 12:48:42,198 23775 [task-scheduler-1] ERROR
o.s.i.handler.LoggingHandler -
org.springframework.messaging.MessagingException: failure occurred
while polling for mail; nested exception is
javax.mail.MessagingException: A7 NO STORE attempt on READ-ONLY folder
(Failure); nested exception is:
com.sun.mail.iap.CommandFailedException: A7 NO STORE attempt on
READ-ONLY folder (Failure) at
org.springframework.integration.mail.MailReceivingMessageSource.doReceive(MailReceivingMessageSource.java:74)
at
org.springframework.integration.endpoint.AbstractMessageSource.receive(AbstractMessageSource.java:167)
at
org.springframework.integration.endpoint.SourcePollingChannelAdapter.receiveMessage(SourcePollingChannelAdapter.java:250)
Now it seems that by default the FOLDER is opened in READ_ONLY way and this seems to cause an error.
I'm stucked here and I can't figure out how to solve the issue.
May anybody give me a tip?
Thank you
Angelo

adapterSpec.get()
Issuing a get() on the spec circumvents Spring's bean initialization logic which switches the folder to read/write.
Either make the adapter a #Bean or simply remove the .get() and Spring will perform the initialization.
.from(adapterSpec, e -> ...

Related

MinIO .Net client API failing with bogus General Exception

MinIO is returning a general exception when calling the API from a .net client. In the library parsing the xml fails telling me that "Client calls PutObjectAsync General Exception 'doctype' is an unexpected token. The expected token is 'DOCTYPE'" which is no help at all.
MinIO Version
2021-09-09T21:37:07Z
Uploading objects using the webconsole works as expected.
Can you share an example of the code you are using? Please make sure you are using the S3 endpoint (running on port 9000 by default) and not the console-ui endpoint (9090 by default).
Here is a simple example of how to use the .net library to connect to the MinIO running on https://play.min.io:9000.
using Minio;
// Initialize the client with access credentials.
private static MinioClient minio = new MinioClient("play.min.io",
"Q3AM3UQ867SPQQA43P2F",
"zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG"
).WithSSL();
// Create an async task for listing buckets.
var getListBucketsTask = minio.ListBucketsAsync();
// Iterate over the list of buckets.
foreach (Bucket bucket in getListBucketsTask.Result.Buckets)
{
Console.WriteLine(bucket.Name + " " + bucket.CreationDateDateTime);
}
Please read more at https://docs.min.io/docs/dotnet-client-quickstart-guide.html

How to fix "Guru Meditation Error" caused by http.GET();?

I want to send data on my web server from esp32. ESP32 can not get to website and reboot.
I used exaples from Arduino IDE.
I've tried to post(htt.POST("X")) something and got same error.
I marked code to find which line cause the problem.
Googled.
...
msg="192.168.4.22/parametr";
if(WiFi.status() == WL_CONNECTED){
if(client.connect(host,httpPort)){
http.begin(msg);
Serial.print("0");
Serial.print("[HTTP] GET...\n");
int httpCode = http.GET();
Serial.print("1");
...
I expect to get to website, but now esp32 reboots when achived http.GET() and never prints "1".
Error message: Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
The URL you are passing to http.begin() is incorrect. You need to include the http:// prefix (see here). Semantically, it makes more sense to name this variable url rather than msg.
Also ... Check the return value of http.begin() - in case it is still failing.
const char *url = "http://192.168.4.22/parametr";
// Check Wi-Fi connected, etc.
if (!http.begin(url)) {
Serial.println("HTTP client failed to connect ...");
}
else {
int httpCode = http.GET();
// etc.
}
The LoadProhibited fatal error indicates an attempt to read or write an invalid memory location. When the library failed to parse the URL you provided, some member of the HTTPClient object may have been left uninitialized.
Log output
To assist your debugging, try setting the 'Core debug level' in the Arduino IDE to 'Debug'.
Arduino IDE -> Tools -> Core Debug Level -> Debug
This will ensure that the ESP log messages - such as might be printed if initialization of the HTTP client fails - will be printed over the serial port.
For example, this is the log output I see if I fail to include the http:// protocol specifier in the URL (after changing the core debug level).

Spring Integration WebFlux Error Handling

SI 5+ supports WebFlux, which means we can now build a reactive messaging system. However, it also means that the design has be thought through, and usual methods of error handling don't work. In a reactive flow, a message is a Publisher(Flux), and it doesn't throw an exception, but emits an error notification. Thus, the error channel header set on the message is useless because SI doesn't know that the Flux resulted in an error.
Consider the following code:
.handle(WebFlux.outboundGateway(m -> m.getPayload().toString(), webClient)
.expectedResponseType(YelpRecord.class)
.httpMethod(GET)
.mappedRequestHeaders(ACCEPT)
.replyPayloadToFlux(true))
.handle((GenericHandler<Flux<YelpRecord>>) (flux, headers) ->
flux
.doOnError(t -> log.error(t.getMessage(), t))
.doAfterTerminate(() ->
log.info("Completed streaming from: {}.", headers.get(DOWNLOAD_URI_HEADER))
)
.onBackpressureBuffer(
yelpArtifactoryProperties.getOnBackpressureBufferSize(),
BufferOverflowStrategy.ERROR)
)
What is missing in the above code snippet is sending the exception to the error channel configured on the message from doOnError. How can we do that?
Does ((MessageChannel) header.getErrorChannel()).send(...) work for you there in the .doOnError()?
The point is that you are right and this Flux in the message payload is already out of control by the Framework and if you would like to deal with its errors, you have to do that yourself. That is already your code with a doOnError() therefore the Framework can't help you already with something automatic.

Couchbase Lite iOS Replication stops replicating after restart

I'm still a bit new to Couchbase and iOS, but I'm running into a problem restarting my replications that I'm having trouble with. Here are a few notes about the flow.
The backend is using custom authentication.
When the user logs in, a new session is created in the sync gateway and those session details are returned to the iOS device. The app then uses those credentials to set up a push and pull replication (I've dropped the push replication for now while trying to debug this). The options on the replications aren't much and are as follows:
let pull = self.database.createPullReplication(self.remoteDBURL);
pull.continuous = true;
pull.headers["uuid"] = "device-1";
pull.setCookieNamed(sessionName, withValue: sessionId, path: "/", expirationDate: cookieExpirationDate, secure: false);
pull.start();
self._pull = pull;
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DataService.replicationChanged(_:)), name: kCBLReplicationChangeNotification, object: self._pull);
This works great and all the proper documents are synced to the device. Currently I have the backend created cookies that only last for about 5 minutes so I can test the refreshing of cookies. So, during the first few minutes, any docs I add to the channel that the app gets, the app receives the doc and all is good.
About halfway into the life of the token, the backend is set up to return with a 401 error telling the app to use it's token to get a new token. So, I have this in the replication change listener:
#objc public func replicationChanged(n: NSNotification) {
let replication = n.object as! CBLReplication;
let error = replication.lastError;
if (error != nil) {
print("last error is NOT nil");
print("last error = \(error)");
switch error!.code {
case 401:
self.updateReplicationSession();
default:
break;
}
}
}
Then, the updateReplication function looks like this:
... make http call to getNewToken url using the 'old' or 'soon to be expired' token. *The server is successfully returning this new session.
self._pull.setCookieNamed(newSessionName, withValue: newSessionId, path: "/", expirationDate: newExpirationDate, secure: false);
self._pull.restart();
...
It is at this time that the syncing stops working. No errors are thrown that I can see of other than once I received a CFNetwork Internal error. I can see on the server logs that the replication sends in the new session once... then everything just seems to hang for the replication. Any new docs to a channel that it gets and it doesn't get them. I don't see anything in the Sync Gateway logs indicating what the problem is. However, I'm still pretty new to this... so there may be something. Additionally, I set up a function to run every few seconds and print the status of the replication and it is stuck on ACTIVE.
I'm using sync gateway v 1.3 and CBL ios 1.3. I was using version 1.2.1 and was having this problem... hoping ugrading to 1.3 would magically fix it. It didn't, but I'm not sure I should go back to 1.2.1.
I'm completely stumped on this. I've searched high and low and often seem to find an answer that fits the bill... but it still doesn't work.
I've tried delaying the updating of the session in order for all calls from replication to 'fail' first. I've tried just calling start() on the replication thinking that maybe the 401 killed the replication and restart() isn't going to do anything. I've called stop()... then waited a bit and called start(). Not sure what to do next.
Any help is appreciated guys! Is it possible the local DB on the phone and the sync gateway have a unresolvable rev problem?
EDIT 1
The only way I can currently get it to work is to completely delete the local db in the replication changed function and restart it... then start a new replication... this works... unfortunately though, I then have to broadcast a notification so that any table view that may be up reloads the query. This causes a refresh animation in tableviews and isn't sustainable... but at least I can keep moving for now.
EDIT 2
I found how to enable better logging of the CBL on iOS and here's an error I keep seeing after a token refresh/ replication restart:
CBLWebSocketChangeTracker[0x7ff9bb53b5e0 iphone]: Connection error #8, retrying in 256.0 sec: PSWebSocketError[3, "Output stream end encountered"]
Thoughts?
EDIT 3
I changed things around a bit to get rid of the refreshing of tokens. I attempted to make it so when a user logs in from the iOS app, the backend creates a session with the sync gateway for that user and returns it. The app then starts a replication with that session. Then, after the 5 minutes (the ttl used when creating the session), the next time the app tries to sync, it will get a 401 and stop the replication and present the login screen. Then, when the user logs in again, a new session is created, etc...
I found 2 things:
-Anytime I added a doc to a channel that would sync with the app, when the app synced, the session expiration date would increase by about 20ish seconds. Is this normal behavior? The only way it would log the user out on the expired token is if I didn't add any docs for long enough.
-The restarted replication still gets stuck. Here are the logs for the sync gateway and xcode:
This is the last part of the sync gateway when the session is expired... which will send the 404 to the app.
2016-08-23T21:11:51.089-05:00 Changes+: Sending seq:163 from channel jmoore2
2016-08-23T21:11:51.089-05:00 Changes+: MultiChangesFeed sending &{Seq:163 ID:un:jmoore2_116 Deleted:false Removed:{} Doc:map[] Changes:[map[rev:1-e775ef6713dc39f6d52d35cefb396fe3]] Err:<nil> allRemoved:false branched:false} (to jmoore2)
2016-08-23T21:11:51.089-05:00 Changes: MultiChangesFeed done (to jmoore2)
2016-08-23T21:11:51.089-05:00 HTTP+: #212: --> 200 OK (0.0 ms)
2016-08-23T21:11:51.459-05:00 HTTP: #216: GET /my_gateway/_session/3fa29222db286e8ec67a51e88b613ba4cd5cbf31 (ADMIN)
2016-08-23T21:11:51.459-05:00 HTTP: #216: --> 404 missing (0.2 ms)
2016-08-23T21:11:51.461-05:00 HTTP: #217: GET /my_gateway/_session/3fa29222db286e8ec67a51e88b613ba4cd5cbf31 (ADMIN)
2016-08-23T21:11:51.461-05:00 HTTP: #217: --> 404 missing (0.2 ms)
Then, when the user logs back in, here are SG log for that:
2016-08-23T21:15:41.200-05:00 HTTP: #223: GET /my_gateway/_session/1a6105eaf2c91de320a47422041c655dd3d5c279 (ADMIN)
2016-08-23T21:15:41.201-05:00 HTTP+: #223: --> 200 (0.5 ms)
2016-08-23T21:15:41.203-05:00 HTTP: #224: GET /my_gateway/_local/78c229762074a95c864f7fecc03ce88f0ef6c499 (as jmoore2)
2016-08-23T21:15:41.203-05:00 HTTP+: #224: --> 200 (0.5 ms)
2016-08-23T21:15:41.371-05:00 Cache: Received #164 after 455ms ("user-login-info:jmoore2" / "48-ea0b2d9771fa2be1d76838f9e4d55081")
2016-08-23T21:15:41.371-05:00 Cache: #164 ==> channel "*"
2016-08-23T21:15:41.371-05:00 Changes+: Notifying that “mdatabase” changed (keys="{*}") count=31
And the xcode log when restarting the replication immediately goes to this:
21:15:41.195‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Reachability state = <mycomp.local>:reachable (30002), suspended=0
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Server is Couchbase Sync Gateway/1.3.0
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/]: Replicating from lastSequence=162
21:15:41.205‖ Sync: CBLRestPuller[http://mycomp.local:8080/iphone/] starting ChangeTracker: mode=3, since=162
21:15:41.207‖ ChangeTracker: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Starting...
21:15:41.207‖ Sync: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: GET //mycomp.local:8080/iphone/_changes?feed=websocket
21:15:41.208‖ ChangeTracker: CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Started... <http://mycomp.local:8080/iphone/_changes?feed=websocket>
21:15:41.211‖ CBLWebSocketChangeTracker[0x7f97d8698190 iphone]: Connection error #1, retrying in 2.0 sec: PSWebSocketError[3, "Output stream end encountered"]
and also sometimes this error shows up:
2016-08-23 21:01:15.232 MyApp[52094:36001215] 52094: CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork_Sim/CFNetwork-758.3.15/Loading/URLConnectionLoader.cpp:289)

Enabling Jersey trace logging causes MaxHeaderCountExceededException

I am trying to debug my jersey 2 app on Payara 162, but on every request, after the trace information is printed I get this exception and the client gets no response:
org.glassfish.grizzly.http.util.MimeHeaders$MaxHeaderCountExceededException: Illegal attempt to exceed the configured maximum number of headers: 100
at org.glassfish.grizzly.http.util.MimeHeaders.createHeader(MimeHeaders.java:396)
at org.glassfish.grizzly.http.util.MimeHeaders.addValue(MimeHeaders.java:422)
at org.glassfish.grizzly.http.HttpHeader.addHeader(HttpHeader.java:707)
at org.glassfish.grizzly.http.server.Response.addHeader(Response.java:1177)
at org.apache.catalina.connector.Response.addHeader(Response.java:1221)
at org.apache.catalina.connector.ResponseFacade.addHeader(ResponseFacade.java:579)
at org.glassfish.jersey.servlet.internal.ResponseWriter.writeResponseStatusAndHeaders(ResponseWriter.java:165)
at org.glassfish.jersey.server.ServerRuntime$Responder$1.getOutputStream(ServerRuntime.java:701)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.flushBuffer(CommittingOutputStream.java:305)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:261)
at org.glassfish.jersey.message.internal.CommittingOutputStream.close(CommittingOutputStream.java:276)
at org.glassfish.jersey.message.internal.OutboundMessageContext.close(OutboundMessageContext.java:839)
at org.glassfish.jersey.server.ContainerResponse.close(ContainerResponse.java:412)
at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:784)
at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:444)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:434)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:329)
In my jersey I app I configured trace as so:
public class RestApplication extends ResourceConfig {
public RestApplication() {
super();
packages(true, "com.example");
register(JacksonFeature.class);
register(JsonProvider.class);
register(RolesAllowedDynamicFeature.class);
property("jersey.config.server.tracing.type", "ON_DEMAND");
property("jersey.config.server.tracing.threshold", "VERBOSE");
}
}
I enabled the logger in my logback.xml (I have configured Payara to use logback), and I see the full trace info in my server log when I enable it on demand by adding the X-Jersey-Tracing-Accept header to my request, but then I get the exception. When I don't add the header to the request everything works but of course I don't get the trace.
I'm wondering if there is anything I can change to fix this or is it a bug?
The problem is that tracing adds a header into the REST response for each event.
Grizzly imposes a limit on number of headers in the response.
Payara Server by default defines 100 as maximum number of headers in the response. You need to increase this number to allow all tracing info in the response.
To set a higher number for maximum number of headers, you need to use asadmin. There is no option to set this in the GUI admin console, it is missing in the screen to configure the HTTP protocol.
If your configuration is named server-config and the network listener is http-listener-1, then execute the following asadmin command to set it to 1000:
asadmin> set
configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.max-response-headers=1000
You can use a similar command to set all Grizzly network listener propertiesoptions, just replace max-response-headers to the name of the option you want to set, using - as a word separator instead of camel case.

Resources