Actualmente estoy desarrollando una aplicación de correo electrónico con un servicio en segundo plano que se utiliza junto con la funcionalidad JavaMail Idle. El servicio en segundo plano mantiene funcionando la funcionalidad inactiva emitiendo un cheque cada 29 minutos (ya que el servidor que se está utilizando (servidor de Exchange)) a veces puede interrumpir la conexión después de 30 minutos de estar conectado.

Si bien esto funciona perfectamente, si el servidor de Exchange está fuera de línea, la aplicación seguirá intentando volver a conectarse a la carpeta IMAP de forma indefinida. He notado picos en el uso de datos entre las 3 a. M. Y las 6 a. M. (Una hora típica de actualización de Exchange).

Para evitar el aumento del uso de datos, estoy buscando implementar una funcionalidad en la que la aplicación debería intentar volver a conectarse a la carpeta IMAP tres veces y luego mostrar una advertencia al usuario de que el servidor está fuera de línea y se reintentará un nuevo intento de conexión en 30 minutos. .

Para lograr esto, ¿cómo podría detectar si el servidor de Exchange está realmente fuera de línea / actualizándose y si la aplicación no se puede conectar a la carpeta IMAP se lanzaría alguna excepción? Como si se lanzara una excepción, entonces podría guardar una variable int local e incrementarla en uno cada vez que se lanza la excepción y luego mostrar la alerta al usuario por tercera vez.

Mi implementación de código actual se puede ver a continuación:

public void checkInboxEmail(final String host, final String user, final String password) {

    Log.d(TAG, "checkEmail");

    this.host = host;
    this.user = user;
    this.password = password;

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Log.d(TAG, "checkEmail - run()");

                long databaseRecords;

                //create properties field
                Properties properties = new Properties();
                properties.put("mail.store.protocol", "imaps");
                properties.put("mail.imaps.ssl.trust", "*");
                properties.put("mail.debug", "true");

                emailSession = Session.getInstance(properties, new Authenticator() {
                    protected PasswordAuthentication getPasswordAuthentication() {
                        return new PasswordAuthentication(user, password);
                    }
                });


                IMAPStore imapStore = (IMAPStore) emailSession.getStore("imaps");
                // imapStore.connect();

                imapStore.connect(host, user, password);

                if (imapStore.isConnected()) {
                    Log.d("MailPush", "Successfully connected to IMAP");
                } else {
                    Log.d("MailPush", "Not connected to IMAP");
                }

                final IMAPFolder folder = (IMAPFolder) imapStore.getFolder("Inbox");
                folder.open(IMAPFolder.READ_WRITE);

                databaseRecords = dbManager.getReceivedEmailRecordsCount();

                if (databaseRecords < folder.getMessageCount()) {
                    Log.d(TAG, "Receiving Mail...");
                    receiveMail(folder.getMessages());
                } else {
                    Log.d(TAG, "Records match.");
                }

                Folder[] fdr = imapStore.getDefaultFolder().list();
                for (Folder fd : fdr)
                    System.out.println(">> " + fd.getName());

                folder.addMessageCountListener(new MessageCountListener() {

                    public void messagesAdded(MessageCountEvent e) {

                        System.out.println("Message Added Event Fired");
                        Log.d(TAG, "MESSAGE TYPE: " + e.getType());
                        //ADDED = 1 & REMOVED = 2

                        try {
                            Message[] messages = e.getMessages();

                            System.out.println("messages.length---" + messages.length);

                            for (Message message : messages) {
                                if (!message.getFlags().contains(Flags.Flag.SEEN)) {

                                    //Message is new (hasn't been seen) > Message Details
                                    System.out.println("---------------------------------");
                                    System.out.println("Email Number " + (message.getMessageNumber()));
                                    System.out.println("Subject: " + message.getSubject());
                                    System.out.println("From: " + message.getFrom()[0]);
                                    System.out.println("Text: " + message.getContent().toString());

                                    String from = message.getFrom()[0].toString();

                                    String cc = InternetAddress.toString(message.getRecipients(Message.RecipientType.CC));
                                    Log.d(TAG, "CC 1: " + cc);

                                    Address[] recipients = message.getRecipients(Message.RecipientType.CC);
                                    cc = InternetAddress.toString(recipients);
                                    Log.d(TAG, "CC 2: " + cc);

                                    //Check Encryption Details > Add SEEN Flag > Add to database
                                    checkEncryption((MimeMessage) message, from, cc);
                                }
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    }

                    public void messagesRemoved(MessageCountEvent e) {
                        System.out.println("Message Removed Event fired");
                    }
                });

                folder.addMessageChangedListener(new MessageChangedListener() {

                    public void messageChanged(MessageChangedEvent e) {
                        System.out.println("Message Changed Event fired");
                    }
                });

                startListening(folder);

                //close the store and folder objects
                //   emailFolder.close(false);
                //   store.close();

            } catch (NoSuchProviderException e) {
                e.printStackTrace();
            } catch (MessagingException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

private void startListening(IMAPFolder imapFolder) {
    Log.d(TAG, "startListening");

    // We need to create a new thread to keep alive the connection
    Thread t = new Thread(
            new KeepAliveRunnable(imapFolder), "IdleConnectionKeepAlive"
    );

    t.start();

    while (!Thread.interrupted()) {
        Log.d(TAG, "Starting IDLE");
        try {
            Log.d(TAG, "Setting IDLE");
            imapFolder.idle();
        } catch (FolderClosedException fex) {
            //Server closes connection.
            Log.d(TAG, "FolderClosedException. Server potentially dropped connection. Retrying connection...");
            fex.printStackTrace();

            if (!isServiceRunning(MyService.class)) {
                Log.d(TAG, "Service isn't running. Starting service...");

                //Start service
                Intent intent = new Intent(context, MyService.class);
                intent.putExtra("host", host);
                intent.putExtra("email", user);
                intent.putExtra("password", password);

                context.startService(intent);
            } else {
                Log.d(TAG, "Service is already running. Checking email...");
                checkInboxEmail(host, user, password);
            }
        } catch (MessagingException e) {
            //Idle isn't supported by server.
            Log.d(TAG, "Messaging exception during IDLE: ");
            e.printStackTrace();
        }
    }

    // Shutdown keep alive thread
    if (t.isAlive()) {
        Log.d(TAG, "Interrupting thread");
        t.interrupt();
    }
}

private static class KeepAliveRunnable implements Runnable {

    private final String TAG = getClass().getName();

    private static final long KEEP_ALIVE_FREQ = 60000 * 29; // 29 minutes (Exchange connection drops after 20-30 minutes)

    private IMAPFolder folder;

    KeepAliveRunnable(IMAPFolder folder) {
        this.folder = folder;
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                Thread.sleep(KEEP_ALIVE_FREQ);

                // Perform a messageCount check just to keep alive the connection
                Log.d(TAG, "Performing a messageCount check to keep the connection alive");
                folder.getMessageCount();
            } catch (InterruptedException e) {
                // Ignore, just aborting the thread...
                Log.d(TAG, "Interrupted...");
                e.printStackTrace();
            } catch (MessagingException e) {
                // Shouldn't really happen...
                Log.d(TAG, "Unexpected exception while keeping alive the IDLE connection");
                e.printStackTrace();
            }
        }
    }
}

private void receiveMail(Message[] messages) {
    try {

        System.out.println("messages.length---" + messages.length);

        for (Message message : messages) {
            if (!message.getFlags().contains(Flags.Flag.SEEN)) {

                //Message is new (hasn't been seen) > Message Details
                System.out.println("---------------------------------");
                System.out.println("Email Number " + (message.getMessageNumber()));
                System.out.println("Subject: " + message.getSubject());
                System.out.println("From: " + message.getFrom()[0]);
                System.out.println("Text: " + message.getContent().toString());

                String from = message.getFrom()[0].toString();
                String cc = InternetAddress.toString(message.getRecipients(Message.RecipientType.CC));

                //Check Encryption Details > Add SEEN Flag > Add to database
                checkEncryption((MimeMessage) message, from, cc);
            }
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
}
0
Toby Clench 15 ene. 2017 a las 23:58

2 respuestas

Si el servidor está activo pero no acepta conexiones, la conexión fallará inmediatamente (con una excepción). Si el servidor está inactivo y ha establecido un tiempo de espera de conexión, la conexión fallará después del tiempo de espera (con una excepción).

1
Bill Shannon 16 ene. 2017 a las 00:31
¿Qué excepciones serán estas?
 – 
Toby Clench
16 ene. 2017 a las 02:18
1
MessagingException, posiblemente alguna subclase como com.sun.mail.util.MailConnectException.
 – 
Bill Shannon
16 ene. 2017 a las 07:35

Para aclarar la respuesta de @ BillShannon, si el host del servidor está activo pero Exchange no acepta conexiones, la conexión fallará inmediatamente con un ConnectException: connection refused. Si el host del servidor está inactivo, la conexión fallará después del tiempo de espera con un ConnectException: connect timeout (o posiblemente un SocketTimeoutException), independientemente de si ha establecido un tiempo de espera de conexión, ya que la plataforma siempre tiene uno.

1
Marquis of Lorne 16 ene. 2017 a las 04:03
@BillShannon, he agregado una MessagingException como captura para mi función 'checkInboxEmail' para que luego se muestre una alerta al usuario (después de que la excepción se haya detectado tres veces), pero ¿cómo puedo evitar que JavaMail intente volver a conectarse? ¿Sería esto solo mediante el uso de los diferentes tiempos de espera y luego usar un controlador para postDelay ejecutando la función hasta 30 minutos de la hora actual?
 – 
Toby Clench
16 ene. 2017 a las 13:04