Synchronize message updates and message operations

pull/50/head
M66B 7 years ago
parent 92e9120e06
commit a46874130f

@ -97,13 +97,13 @@ import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class ServiceSynchronize extends LifecycleService { public class ServiceSynchronize extends LifecycleService {
private final Object lock = new Object();
private ExecutorService executor = Executors.newSingleThreadExecutor(); private ExecutorService executor = Executors.newSingleThreadExecutor();
private static final int NOTIFICATION_SYNCHRONIZE = 1; private static final int NOTIFICATION_SYNCHRONIZE = 1;
private static final int NOTIFICATION_UNSEEN = 2; private static final int NOTIFICATION_UNSEEN = 2;
private static final long NOOP_INTERVAL = 9 * 60 * 1000L; // ms private static final long NOOP_INTERVAL = 9 * 60 * 1000L; // ms
private static final int FETCH_BATCH_SIZE = 10;
private static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes private static final int ATTACHMENT_BUFFER_SIZE = 8192; // bytes
static final String ACTION_PROCESS_OPERATIONS = BuildConfig.APPLICATION_ID + ".PROCESS_OPERATIONS"; static final String ACTION_PROCESS_OPERATIONS = BuildConfig.APPLICATION_ID + ".PROCESS_OPERATIONS";
@ -607,49 +607,53 @@ public class ServiceSynchronize extends LifecycleService {
ifolder.addMessageCountListener(new MessageCountAdapter() { ifolder.addMessageCountListener(new MessageCountAdapter() {
@Override @Override
public void messagesAdded(MessageCountEvent e) { public void messagesAdded(MessageCountEvent e) {
try { synchronized (lock) {
Log.i(Helper.TAG, folder.name + " messages added");
for (Message imessage : e.getMessages())
synchronizeMessage(folder, ifolder, (IMAPMessage) imessage);
} catch (MessageRemovedException ex) {
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
} catch (Throwable ex) {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
// Cascade up
try { try {
istore.close(); Log.i(Helper.TAG, folder.name + " messages added");
} catch (MessagingException e1) { for (Message imessage : e.getMessages())
Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1)); synchronizeMessage(folder, ifolder, (IMAPMessage) imessage);
} catch (MessageRemovedException ex) {
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
} catch (Throwable ex) {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
// Cascade up
try {
istore.close();
} catch (MessagingException e1) {
Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1));
}
} }
} }
} }
@Override @Override
public void messagesRemoved(MessageCountEvent e) { public void messagesRemoved(MessageCountEvent e) {
try { synchronized (lock) {
Log.i(Helper.TAG, folder.name + " messages removed"); try {
for (Message imessage : e.getMessages()) Log.i(Helper.TAG, folder.name + " messages removed");
try { for (Message imessage : e.getMessages())
long uid = ifolder.getUID(imessage); try {
long uid = ifolder.getUID(imessage);
DB db = DB.getInstance(ServiceSynchronize.this); DB db = DB.getInstance(ServiceSynchronize.this);
int count = db.message().deleteMessage(folder.id, uid); int count = db.message().deleteMessage(folder.id, uid);
Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count); Log.i(Helper.TAG, "Deleted uid=" + uid + " count=" + count);
} catch (MessageRemovedException ex) { } catch (MessageRemovedException ex) {
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex)); Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
} }
} catch (Throwable ex) { } catch (Throwable ex) {
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex)); Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex); reportError(account.name, folder.name, ex);
// Cascade up // Cascade up
try { try {
istore.close(); istore.close();
} catch (MessagingException e1) { } catch (MessagingException e1) {
Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1)); Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1));
}
} }
} }
} }
@ -664,23 +668,25 @@ public class ServiceSynchronize extends LifecycleService {
ifolder.addMessageChangedListener(new MessageChangedListener() { ifolder.addMessageChangedListener(new MessageChangedListener() {
@Override @Override
public void messageChanged(MessageChangedEvent e) { public void messageChanged(MessageChangedEvent e) {
try { synchronized (lock) {
Log.i(Helper.TAG, folder.name + " message changed"); try {
synchronizeMessage(folder, ifolder, (IMAPMessage) e.getMessage()); Log.i(Helper.TAG, folder.name + " message changed");
} catch (MessageRemovedException ex) { synchronizeMessage(folder, ifolder, (IMAPMessage) e.getMessage());
Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex)); } catch (MessageRemovedException ex) {
} catch (Throwable ex) { Log.w(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex)); } catch (Throwable ex) {
reportError(account.name, folder.name, ex); Log.e(Helper.TAG, folder.name + " " + ex + "\n" + Log.getStackTraceString(ex));
reportError(account.name, folder.name, ex);
folder.error = Helper.formatThrowable(ex); folder.error = Helper.formatThrowable(ex);
DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder); DB.getInstance(ServiceSynchronize.this).folder().updateFolder(folder);
// Cascade up // Cascade up
try { try {
istore.close(); istore.close();
} catch (MessagingException e1) { } catch (MessagingException e1) {
Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1)); Log.w(Helper.TAG, folder.name + " " + e1 + "\n" + Log.getStackTraceString(e1));
}
} }
} }
} }
@ -734,82 +740,84 @@ public class ServiceSynchronize extends LifecycleService {
} }
private void processOperations(EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder) throws MessagingException, JSONException, IOException { private void processOperations(EntityFolder folder, Session isession, IMAPStore istore, IMAPFolder ifolder) throws MessagingException, JSONException, IOException {
try { synchronized (lock) {
Log.i(Helper.TAG, folder.name + " start process"); try {
Log.i(Helper.TAG, folder.name + " start process");
DB db = DB.getInstance(this);
List<EntityOperation> ops = db.operation().getOperationsByFolder(folder.id);
Log.i(Helper.TAG, folder.name + " pending operations=" + ops.size());
for (EntityOperation op : ops)
try {
Log.i(Helper.TAG, folder.name +
" start op=" + op.id + "/" + op.name +
" msg=" + op.message +
" args=" + op.args);
EntityMessage message = db.message().getMessage(op.message);
if (message == null)
throw new MessageRemovedException();
DB db = DB.getInstance(this);
List<EntityOperation> ops = db.operation().getOperationsByFolder(folder.id);
Log.i(Helper.TAG, folder.name + " pending operations=" + ops.size());
for (EntityOperation op : ops)
try { try {
JSONArray jargs = new JSONArray(op.args); Log.i(Helper.TAG, folder.name +
" start op=" + op.id + "/" + op.name +
" msg=" + op.message +
" args=" + op.args);
if (EntityOperation.SEEN.equals(op.name)) EntityMessage message = db.message().getMessage(op.message);
doSeen(folder, ifolder, message, jargs); if (message == null)
throw new MessageRemovedException();
else if (EntityOperation.ADD.equals(op.name)) try {
doAdd(folder, ifolder, message, db); JSONArray jargs = new JSONArray(op.args);
else if (EntityOperation.MOVE.equals(op.name)) if (EntityOperation.SEEN.equals(op.name))
doMove(folder, isession, istore, ifolder, message, jargs, db); doSeen(folder, ifolder, message, jargs);
else if (EntityOperation.DELETE.equals(op.name)) else if (EntityOperation.ADD.equals(op.name))
doDelete(folder, ifolder, message, jargs, db); doAdd(folder, ifolder, message, db);
else if (EntityOperation.SEND.equals(op.name)) else if (EntityOperation.MOVE.equals(op.name))
doSend(db, message); doMove(folder, isession, istore, ifolder, message, jargs, db);
else if (EntityOperation.ATTACHMENT.equals(op.name)) else if (EntityOperation.DELETE.equals(op.name))
doAttachment(folder, op, ifolder, message, jargs, db); doDelete(folder, ifolder, message, jargs, db);
else else if (EntityOperation.SEND.equals(op.name))
throw new MessagingException("Unknown operation name=" + op.name); doSend(db, message);
// Operation succeeded else if (EntityOperation.ATTACHMENT.equals(op.name))
db.operation().deleteOperation(op.id); doAttachment(folder, op, ifolder, message, jargs, db);
} catch (Throwable ex) {
message.error = Helper.formatThrowable(ex);
db.message().updateMessage(message);
if (BuildConfig.DEBUG && ex instanceof NullPointerException) { else
throw new MessagingException("Unknown operation name=" + op.name);
// Operation succeeded
db.operation().deleteOperation(op.id); db.operation().deleteOperation(op.id);
throw ex; } catch (Throwable ex) {
} message.error = Helper.formatThrowable(ex);
db.message().updateMessage(message);
if (ex instanceof MessageRemovedException || if (BuildConfig.DEBUG && ex instanceof NullPointerException) {
ex instanceof FolderNotFoundException || db.operation().deleteOperation(op.id);
ex instanceof SMTPSendFailedException) { throw ex;
Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); }
// There is no use in repeating if (ex instanceof MessageRemovedException ||
db.operation().deleteOperation(op.id); ex instanceof FolderNotFoundException ||
continue; ex instanceof SMTPSendFailedException) {
} else if (ex instanceof MessagingException) { Log.w(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
// Socket timeout is a recoverable condition (send message)
if (ex.getCause() instanceof SocketTimeoutException) { // There is no use in repeating
Log.w(Helper.TAG, "Recoverable " + ex); db.operation().deleteOperation(op.id);
// No need to inform user continue;
return; } else if (ex instanceof MessagingException) {
// Socket timeout is a recoverable condition (send message)
if (ex.getCause() instanceof SocketTimeoutException) {
Log.w(Helper.TAG, "Recoverable " + ex);
// No need to inform user
return;
}
} }
}
throw ex; throw ex;
}
} finally {
Log.i(Helper.TAG, folder.name + " end op=" + op.id + "/" + op.name);
} }
} finally { } finally {
Log.i(Helper.TAG, folder.name + " end op=" + op.id + "/" + op.name); Log.i(Helper.TAG, folder.name + " end process");
} }
} finally {
Log.i(Helper.TAG, folder.name + " end process");
} }
} }

Loading…
Cancel
Save