2 * This file is part of NeverNote
\r
3 * Copyright 2009 Randy Baumgarte
\r
5 * This file may be licensed under the terms of of the
\r
6 * GNU General Public License Version 2 (the ``GPL'').
\r
8 * Software distributed under the License is distributed
\r
9 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
\r
10 * express or implied. See the GPL for the specific language
\r
11 * governing rights and limitations.
\r
13 * You should have received a copy of the GPL along with this
\r
14 * program. If not, go to http://www.gnu.org/licenses/gpl.html
\r
15 * or write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
19 package cx.fbn.nevernote.threads;
\r
21 import java.net.UnknownHostException;
\r
22 import java.util.ArrayList;
\r
23 import java.util.Calendar;
\r
24 import java.util.Date;
\r
25 import java.util.GregorianCalendar;
\r
26 import java.util.List;
\r
27 import java.util.Vector;
\r
28 import java.util.concurrent.LinkedBlockingQueue;
\r
30 import org.apache.thrift.TException;
\r
31 import org.apache.thrift.protocol.TBinaryProtocol;
\r
32 import org.apache.thrift.transport.THttpClient;
\r
33 import org.apache.thrift.transport.TTransportException;
\r
35 import com.evernote.edam.error.EDAMNotFoundException;
\r
36 import com.evernote.edam.error.EDAMSystemException;
\r
37 import com.evernote.edam.error.EDAMUserException;
\r
38 import com.evernote.edam.notestore.NoteStore;
\r
39 import com.evernote.edam.notestore.SyncChunk;
\r
40 import com.evernote.edam.notestore.SyncState;
\r
41 import com.evernote.edam.type.Data;
\r
42 import com.evernote.edam.type.Note;
\r
43 import com.evernote.edam.type.Notebook;
\r
44 import com.evernote.edam.type.Resource;
\r
45 import com.evernote.edam.type.SavedSearch;
\r
46 import com.evernote.edam.type.Tag;
\r
47 import com.evernote.edam.type.User;
\r
48 import com.evernote.edam.userstore.AuthenticationResult;
\r
49 import com.evernote.edam.userstore.UserStore;
\r
50 import com.trolltech.qt.core.QObject;
\r
51 import com.trolltech.qt.gui.QMessageBox;
\r
53 import cx.fbn.nevernote.Global;
\r
54 import cx.fbn.nevernote.signals.NoteIndexSignal;
\r
55 import cx.fbn.nevernote.signals.NoteResourceSignal;
\r
56 import cx.fbn.nevernote.signals.NoteSignal;
\r
57 import cx.fbn.nevernote.signals.NotebookSignal;
\r
58 import cx.fbn.nevernote.signals.SavedSearchSignal;
\r
59 import cx.fbn.nevernote.signals.StatusSignal;
\r
60 import cx.fbn.nevernote.signals.SyncSignal;
\r
61 import cx.fbn.nevernote.signals.TagSignal;
\r
62 import cx.fbn.nevernote.sql.DatabaseConnection;
\r
63 import cx.fbn.nevernote.sql.runners.DeletedItemRecord;
\r
64 import cx.fbn.nevernote.utilities.ApplicationLogger;
\r
66 public class SyncRunner extends QObject implements Runnable {
\r
68 private final ApplicationLogger logger;
\r
69 private final DatabaseConnection conn;
\r
70 private boolean idle;
\r
71 private boolean error;
\r
72 public volatile boolean isConnected;
\r
73 public volatile boolean keepRunning;
\r
74 public volatile String authToken;
\r
75 private long evernoteUpdateCount;
\r
77 public volatile NoteStore.Client noteStore;
\r
78 private UserStore.Client userStore;
\r
80 public volatile StatusSignal status;
\r
81 public volatile TagSignal tagSignal;
\r
82 public volatile NotebookSignal notebookSignal;
\r
83 public volatile NoteIndexSignal noteIndexSignal;
\r
84 public volatile NoteSignal noteSignal;
\r
85 public volatile SavedSearchSignal searchSignal;
\r
86 public volatile NoteResourceSignal resourceSignal;
\r
87 public volatile SyncSignal syncSignal;
\r
88 public volatile boolean authRefreshNeeded;
\r
89 public volatile boolean syncNeeded;
\r
90 public volatile boolean disableUploads;
\r
91 public volatile boolean syncDeletedContent;
\r
92 private volatile Vector<String> dirtyNoteGuids;
\r
94 public volatile String username = "";
\r
95 public volatile String password = "";
\r
96 public volatile String userStoreUrl;
\r
97 private final static String consumerKey = "baumgarte";
\r
98 private final static String consumerSecret = "eb8b5740e17cb55f";
\r
99 public String noteStoreUrlBase;
\r
100 private THttpClient userStoreTrans;
\r
101 private TBinaryProtocol userStoreProt;
\r
102 private AuthenticationResult authResult;
\r
103 private User user;
\r
104 private long authTimeRemaining;
\r
105 public long authRefreshTime;
\r
106 public long failedRefreshes = 0;
\r
107 public THttpClient noteStoreTrans;
\r
108 public TBinaryProtocol noteStoreProt;
\r
109 public String noteStoreUrl;
\r
110 public long sequenceDate;
\r
111 public int updateSequenceNumber;
\r
112 private boolean refreshNeeded;
\r
113 private volatile LinkedBlockingQueue<String> workQueue;
\r
114 // private static int MAX_EMPTY_QUEUE_COUNT = 1;
\r
115 private static int MAX_QUEUED_WAITING = 1000;
\r
119 public SyncRunner(String logname) {
\r
120 logger = new ApplicationLogger(logname);
\r
122 noteSignal = new NoteSignal();
\r
123 status = new StatusSignal();
\r
124 tagSignal = new TagSignal();
\r
125 notebookSignal = new NotebookSignal();
\r
126 noteIndexSignal = new NoteIndexSignal();
\r
127 noteSignal = new NoteSignal();
\r
128 searchSignal = new SavedSearchSignal();
\r
129 syncSignal = new SyncSignal();
\r
130 resourceSignal = new NoteResourceSignal();
\r
132 // this.setAutoDelete(false);
\r
133 conn = new DatabaseConnection(logger, Global.syncThreadId);
\r
134 isConnected = false;
\r
135 syncNeeded = false;
\r
136 authRefreshNeeded = false;
\r
137 keepRunning = true;
\r
142 disableUploads = false;
\r
143 // setAutoDelete(false);
\r
144 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);
\r
147 public void run() {
\r
149 logger.log(logger.EXTREME, "Starting thread");
\r
150 while(keepRunning) {
\r
151 String work = workQueue.take();
\r
152 logger.log(logger.EXTREME, "Work found: " +work);
\r
153 if (work.equalsIgnoreCase("stop"))
\r
157 if (authRefreshNeeded == true) {
\r
158 logger.log(logger.EXTREME, "Refreshing connection");
\r
159 refreshConnection();
\r
162 logger.log(logger.EXTREME, "SyncNeeded is true");
\r
163 refreshNeeded=false;
\r
164 sequenceDate = conn.getSyncTable().getLastSequenceDate();
\r
165 updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();
\r
167 logger.log(logger.EXTREME, "Beginning sync");
\r
169 logger.log(logger.EXTREME, "Sync finished");
\r
170 } catch (UnknownHostException e) {
\r
171 status.message.emit(e.getMessage());
\r
174 dirtyNoteGuids = null;
\r
176 logger.log(logger.EXTREME, "Signaling refresh finished. refreshNeeded=" +refreshNeeded);
\r
177 syncSignal.finished.emit(refreshNeeded);
\r
180 catch (InterruptedException e1) {
\r
181 e1.printStackTrace();
\r
186 public DatabaseConnection getConnection() {
\r
190 public boolean isIdle() {
\r
195 public void setConnected(boolean c) {
\r
198 public void setKeepRunning(boolean r) {
\r
199 logger.log(logger.EXTREME, "Setting keepRunning=" +r);
\r
202 public void setNoteStore(NoteStore.Client c) {
\r
203 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");
\r
206 public void setUserStore(UserStore.Client c) {
\r
207 logger.log(logger.EXTREME, "Setting UserStore in sync thread");
\r
211 public void setEvernoteUpdateCount(long s) {
\r
212 logger.log(logger.EXTREME, "Setting Update Count in sync thread");
\r
213 evernoteUpdateCount = s;
\r
216 //***************************************************************
\r
217 //***************************************************************
\r
218 //** These functions deal with Evernote communications
\r
219 //***************************************************************
\r
220 //***************************************************************
\r
221 // Synchronize changes with Evernote
\r
222 @SuppressWarnings("unused")
\r
223 private void evernoteSync() throws java.net.UnknownHostException {
\r
224 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");
\r
226 if (isConnected && keepRunning) {
\r
228 logger.log(logger.EXTREME, "Synchronizing with Evernote");
\r
229 status.message.emit("Synchronizing with Evernote");
\r
231 // Get user information
\r
233 logger.log(logger.EXTREME, "getting user from userstore");
\r
234 User user = userStore.getUser(authToken);
\r
235 logger.log(logger.EXTREME, "Saving user information");
\r
236 syncSignal.saveUserInformation.emit(user);
\r
237 } catch (EDAMUserException e1) {
\r
238 e1.printStackTrace();
\r
239 status.message.emit("User exception getting user account information. Aborting sync and disconnecting");
\r
240 syncSignal.errorDisconnect.emit();
\r
243 } catch (EDAMSystemException e1) {
\r
244 e1.printStackTrace();
\r
245 status.message.emit("System error user account information. Aborting sync and disconnecting!");
\r
246 syncSignal.errorDisconnect.emit();
\r
249 } catch (TException e1) {
\r
250 e1.printStackTrace();
\r
251 syncSignal.errorDisconnect.emit();
\r
252 status.message.emit("Transaction error getting user account information. Aborting sync and disconnecting!");
\r
258 SyncState syncState = null;
\r
260 logger.log(logger.EXTREME, "Getting sync state");
\r
261 syncState = noteStore.getSyncState(authToken);
\r
262 syncSignal.saveUploadAmount.emit(syncState.getUploaded());
\r
263 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());
\r
264 evernoteUpdateCount = syncState.getUpdateCount();
\r
265 } catch (EDAMUserException e) {
\r
266 e.printStackTrace();
\r
267 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");
\r
268 syncSignal.errorDisconnect.emit();
\r
271 } catch (EDAMSystemException e) {
\r
272 e.printStackTrace();
\r
273 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");
\r
274 syncSignal.errorDisconnect.emit();
\r
277 } catch (TException e) {
\r
278 e.printStackTrace();
\r
279 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");
\r
280 syncSignal.errorDisconnect.emit();
\r
285 if (syncState == null) {
\r
286 logger.log(logger.EXTREME, "Sync State is null");
\r
287 status.message.emit("Syncronization Error!");
\r
291 // Determine what to do.
\r
292 // If we need to do a full sync.
\r
293 logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());
\r
294 logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);
\r
295 if (syncState.getFullSyncBefore() > sequenceDate) {
\r
296 logger.log(logger.EXTREME, "Full sequence date has expired");
\r
298 conn.getSyncTable().setLastSequenceDate(0);
\r
299 updateSequenceNumber = 0;
\r
300 conn.getSyncTable().setUpdateSequenceNumber(0);
\r
303 // If there are remote changes
\r
304 logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());
\r
305 logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);
\r
307 if (syncState.getUpdateCount() > updateSequenceNumber) {
\r
308 logger.log(logger.EXTREME, "Refresh needed is true");
\r
309 refreshNeeded = true;
\r
310 logger.log(logger.EXTREME, "Downloading changes");
\r
311 syncRemoteToLocal();
\r
314 if (!disableUploads) {
\r
315 logger.log(logger.EXTREME, "Uploading changes");
\r
316 // Synchronize remote changes
\r
322 syncLocalNotebooks();
\r
324 syncDeletedNotes();
\r
328 syncLocalSavedSearches();
\r
331 syncSignal.refreshLists.emit();
\r
333 logger.log(logger.EXTREME, "Sync completed. Errors=" +error);
\r
334 if (!disableUploads)
\r
335 status.message.emit("Synchronizing complete");
\r
337 status.message.emit("Download syncronization complete. Uploads have been disabled.");
\r
339 logger.log(logger.EXTREME, "Saving sync time");
\r
340 if (syncState.getCurrentTime() > sequenceDate)
\r
341 sequenceDate = syncState.getCurrentTime();
\r
342 if (syncState.getUpdateCount() > updateSequenceNumber)
\r
343 updateSequenceNumber = syncState.getUpdateCount();
\r
344 conn.getSyncTable().setLastSequenceDate(sequenceDate);
\r
345 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
348 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");
\r
350 // Sync deleted items with Evernote
\r
351 private void syncExpunged() {
\r
352 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");
\r
354 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();
\r
355 boolean error = false;
\r
356 for (int i=0; i<expunged.size(); i++) {
\r
358 if (authRefreshNeeded)
\r
359 refreshConnection();
\r
362 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {
\r
363 logger.log(logger.EXTREME, "Tag expunged");
\r
364 updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);
\r
365 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
368 if (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {
\r
369 logger.log(logger.EXTREME, "Notebook expunged");
\r
370 updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);
\r
371 conn.getSyncTable().setLastSequenceDate(sequenceDate);
\r
373 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {
\r
374 logger.log(logger.EXTREME, "Note expunged");
\r
375 updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);
\r
376 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
378 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {
\r
379 logger.log(logger.EXTREME, "saved search expunged");
\r
380 updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);
\r
381 conn.getSyncTable().setLastSequenceDate(sequenceDate);
\r
383 } catch (EDAMUserException e) {
\r
384 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid);
\r
385 logger.log(logger.LOW, e.getStackTrace());
\r
387 } catch (EDAMSystemException e) {
\r
388 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);
\r
389 logger.log(logger.LOW, e.getStackTrace());
\r
391 } catch (EDAMNotFoundException e) {
\r
392 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);
\r
393 // logger.log(logger.LOW, e.getStackTrace());
\r
395 } catch (TException e) {
\r
396 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);
\r
397 logger.log(logger.LOW, e.getStackTrace());
\r
402 conn.getDeletedTable().expungeAllDeletedRecords();
\r
404 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");
\r
407 private void syncDeletedNotes() {
\r
408 if (syncDeletedContent)
\r
410 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");
\r
411 status.message.emit("Synchronizing deleted notes.");
\r
413 List<Note> notes = conn.getNoteTable().getDirty();
\r
414 // Sync the local notebooks with Evernote's
\r
415 for (int i=0; i<notes.size() && keepRunning; i++) {
\r
417 if (authRefreshNeeded)
\r
418 refreshConnection();
\r
420 Note enNote = notes.get(i);
\r
422 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {
\r
423 if (syncDeletedContent) {
\r
424 logger.log(logger.EXTREME, "Deleted note found & synch content selected");
\r
425 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);
\r
426 delNote = getNoteContent(delNote);
\r
427 delNote = noteStore.updateNote(authToken, delNote);
\r
428 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());
\r
429 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
\r
431 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");
\r
432 int usn = noteStore.deleteNote(authToken, enNote.getGuid());
\r
433 enNote.setUpdateSequenceNum(usn);
\r
434 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
\r
436 logger.log(logger.EXTREME, "Resetting deleted dirty flag");
\r
437 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
\r
438 updateSequenceNumber = enNote.getUpdateSequenceNum();
\r
439 logger.log(logger.EXTREME, "Saving sequence number");
\r
440 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
442 } catch (EDAMUserException e) {
\r
443 //logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
\r
444 //status.message.emit("Error sending local note: " +e.getParameter());
\r
445 //logger.log(logger.LOW, e.toString());
\r
447 } catch (EDAMSystemException e) {
\r
448 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
\r
449 status.message.emit("Error: " +e);
\r
450 logger.log(logger.LOW, e.toString());
\r
452 } catch (EDAMNotFoundException e) {
\r
453 //logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
\r
454 //status.message.emit("Error deleting local note: " +e +" - Continuing");
\r
455 //logger.log(logger.LOW, e.toString());
\r
457 } catch (TException e) {
\r
458 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
\r
459 status.message.emit("Error sending local note: " +e);
\r
460 logger.log(logger.LOW, e.toString());
\r
465 // Sync notes with Evernote
\r
466 private void syncLocalNotes() {
\r
467 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
\r
468 status.message.emit("Sending local notes.");
\r
470 List<Note> notes = conn.getNoteTable().getDirty();
\r
471 // Sync the local notebooks with Evernote's
\r
472 for (int i=0; i<notes.size() && keepRunning; i++) {
\r
474 if (authRefreshNeeded)
\r
475 refreshConnection();
\r
477 Note enNote = notes.get(i);
\r
478 if (enNote.isActive()) {
\r
480 logger.log(logger.EXTREME, "Active dirty note found - non new");
\r
481 if (enNote.getUpdateSequenceNum() > 0) {
\r
482 enNote = getNoteContent(enNote);
\r
483 logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
\r
484 enNote = noteStore.updateNote(authToken, enNote);
\r
486 logger.log(logger.EXTREME, "Active dirty found - new note");
\r
487 logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");
\r
488 String oldGuid = enNote.getGuid();
\r
489 enNote = getNoteContent(enNote);
\r
490 enNote = noteStore.createNote(authToken, enNote);
\r
491 noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());
\r
492 conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());
\r
494 updateSequenceNumber = enNote.getUpdateSequenceNum();
\r
495 logger.log(logger.EXTREME, "Saving note");
\r
496 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());
\r
497 List<Resource> rl = enNote.getResources();
\r
498 logger.log(logger.EXTREME, "Getting note resources");
\r
499 for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {
\r
500 Resource newRes = rl.get(j);
\r
501 Data d = newRes.getData();
\r
503 logger.log(logger.EXTREME, "Calculating resource hash");
\r
504 String hash = byteArrayToHexString(d.getBodyHash());
\r
505 logger.log(logger.EXTREME, "updating resources by hash");
\r
506 String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);
\r
507 conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);
\r
508 resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());
\r
511 logger.log(logger.EXTREME, "Resetting note dirty flag");
\r
512 conn.getNoteTable().resetDirtyFlag(enNote.getGuid());
\r
513 updateSequenceNumber = enNote.getUpdateSequenceNum();
\r
514 logger.log(logger.EXTREME, "Emitting note sequence number change");
\r
515 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
517 } catch (EDAMUserException e) {
\r
518 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);
\r
519 status.message.emit("Error sending local note: " +e.getParameter());
\r
520 logger.log(logger.LOW, e.toString());
\r
522 } catch (EDAMSystemException e) {
\r
523 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);
\r
524 status.message.emit("Error: " +e);
\r
525 logger.log(logger.LOW, e.toString());
\r
527 } catch (EDAMNotFoundException e) {
\r
528 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);
\r
529 status.message.emit("Error sending local note: " +e);
\r
530 logger.log(logger.LOW, e.toString());
\r
532 } catch (TException e) {
\r
533 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);
\r
534 status.message.emit("Error sending local note: " +e);
\r
535 logger.log(logger.LOW, e.toString());
\r
540 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");
\r
543 // Sync Notebooks with Evernote
\r
544 private void syncLocalNotebooks() {
\r
545 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");
\r
547 status.message.emit("Sending local notebooks.");
\r
548 List<Notebook> remoteList = new ArrayList<Notebook>();
\r
550 logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");
\r
551 remoteList = noteStore.listNotebooks(authToken);
\r
552 } catch (EDAMUserException e1) {
\r
553 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");
\r
554 status.message.emit("Error: " +e1);
\r
555 logger.log(logger.LOW, e1.toString());
\r
557 } catch (EDAMSystemException e1) {
\r
558 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");
\r
559 status.message.emit("Error: " +e1);
\r
560 logger.log(logger.LOW, e1.toString());
\r
562 } catch (TException e1) {
\r
563 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");
\r
564 status.message.emit("Error: " +e1);
\r
565 logger.log(logger.LOW, e1.toString());
\r
568 logger.log(logger.EXTREME, "Getting local dirty notebooks");
\r
569 List<Notebook> notebooks = conn.getNotebookTable().getDirty();
\r
571 // Sync the local notebooks with Evernote's
\r
572 for (int i=0; i<notebooks.size() && keepRunning; i++) {
\r
574 if (authRefreshNeeded)
\r
575 refreshConnection();
\r
577 Notebook enNotebook = notebooks.get(i);
\r
579 if (enNotebook.getUpdateSequenceNum() > 0) {
\r
580 logger.log(logger.EXTREME, "Existing notebook is dirty");
\r
581 sequence = noteStore.updateNotebook(authToken, enNotebook);
\r
583 logger.log(logger.EXTREME, "New dirty notebook found");
\r
584 String oldGuid = enNotebook.getGuid();
\r
585 boolean found = false;
\r
587 // Look for a notebook with the same name. If one is found, we don't need
\r
588 // to create another one
\r
589 logger.log(logger.EXTREME, "Looking for matching notebook name");
\r
590 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
\r
591 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {
\r
592 enNotebook = remoteList.get(k);
\r
593 logger.log(logger.EXTREME, "Matching notebook found");
\r
598 enNotebook = noteStore.createNotebook(authToken, enNotebook);
\r
600 logger.log(logger.EXTREME, "Updating notebook in database");
\r
601 conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());
\r
602 sequence = enNotebook.getUpdateSequenceNum();
\r
604 logger.log(logger.EXTREME, "Updating notebook sequence in database");
\r
605 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);
\r
606 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");
\r
607 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());
\r
608 updateSequenceNumber = sequence;
\r
609 logger.log(logger.EXTREME, "Emitting sequence number to main thread");
\r
610 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
611 } catch (EDAMUserException e) {
\r
612 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");
\r
613 logger.log(logger.LOW, e.toString());
\r
615 } catch (EDAMSystemException e) {
\r
616 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");
\r
617 logger.log(logger.LOW, e.toString());
\r
619 } catch (EDAMNotFoundException e) {
\r
620 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");
\r
621 logger.log(logger.LOW, e.toString());
\r
623 } catch (TException e) {
\r
624 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");
\r
625 logger.log(logger.LOW, e.toString());
\r
629 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");
\r
632 // Sync Tags with Evernote
\r
633 private void syncLocalTags() {
\r
634 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");
\r
635 List<Tag> remoteList = new ArrayList<Tag>();
\r
636 status.message.emit("Sending local tags.");
\r
639 logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");
\r
640 remoteList = noteStore.listTags(authToken);
\r
641 } catch (EDAMUserException e1) {
\r
642 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");
\r
643 status.message.emit("Error: " +e1);
\r
644 logger.log(logger.LOW, e1.toString());
\r
646 } catch (EDAMSystemException e1) {
\r
647 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");
\r
648 status.message.emit("Error: " +e1);
\r
649 logger.log(logger.LOW, e1.toString());
\r
651 } catch (TException e1) {
\r
652 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");
\r
653 status.message.emit("Error: " +e1);
\r
654 logger.log(logger.LOW, e1.toString());
\r
660 Tag enTag = findNextTag();
\r
661 while(enTag!=null) {
\r
662 if (authRefreshNeeded)
\r
663 refreshConnection();
\r
666 if (enTag.getUpdateSequenceNum() > 0) {
\r
667 logger.log(logger.EXTREME, "Updating tag");
\r
668 sequence = noteStore.updateTag(authToken, enTag);
\r
671 // Look for a tag with the same name. If one is found, we don't need
\r
672 // to create another one
\r
673 logger.log(logger.EXTREME, "New tag. Comparing with remote names");
\r
674 boolean found = false;
\r
675 String oldGuid = enTag.getGuid();
\r
676 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
\r
677 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {
\r
678 conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());
\r
679 enTag = remoteList.get(k);
\r
680 logger.log(logger.EXTREME, "Matching tag name found");
\r
685 enTag = noteStore.createTag(authToken, enTag);
\r
687 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));
\r
688 sequence = enTag.getUpdateSequenceNum();
\r
689 if (!oldGuid.equals(enTag.getGuid())) {
\r
690 logger.log(logger.EXTREME, "Updating tag guid");
\r
691 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());
\r
694 logger.log(logger.EXTREME, "Updating tag sequence number");
\r
695 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);
\r
696 logger.log(logger.EXTREME, "Resetting tag dirty flag");
\r
697 conn.getTagTable().resetDirtyFlag(enTag.getGuid());
\r
698 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
\r
699 updateSequenceNumber = sequence;
\r
700 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
701 } catch (EDAMUserException e) {
\r
702 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");
\r
703 logger.log(logger.LOW, e.toString());
\r
705 } catch (EDAMSystemException e) {
\r
706 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");
\r
707 logger.log(logger.LOW, e.toString());
\r
709 } catch (EDAMNotFoundException e) {
\r
710 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");
\r
711 logger.log(logger.LOW, e.toString());
\r
713 } catch (TException e) {
\r
714 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");
\r
715 logger.log(logger.LOW, e.toString());
\r
719 // Find the next tag
\r
720 logger.log(logger.EXTREME, "Finding next tag");
\r
721 enTag = findNextTag();
\r
723 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");
\r
725 // Sync Tags with Evernote
\r
726 private void syncLocalSavedSearches() {
\r
727 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
\r
728 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();
\r
729 status.message.emit("Sending saved searches.");
\r
731 logger.log(logger.EXTREME, "Getting saved searches to compare with local");
\r
733 remoteList = noteStore.listSearches(authToken);
\r
734 } catch (EDAMUserException e1) {
\r
735 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");
\r
736 status.message.emit("Error: " +e1);
\r
737 logger.log(logger.LOW, e1.toString());
\r
739 } catch (EDAMSystemException e1) {
\r
740 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");
\r
741 status.message.emit("Error: " +e1);
\r
742 logger.log(logger.LOW, e1.toString());
\r
744 } catch (TException e1) {
\r
745 logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");
\r
746 status.message.emit("Error: " +e1);
\r
747 logger.log(logger.LOW, e1.toString());
\r
751 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();
\r
753 // Sync the local notebooks with Evernote's
\r
754 logger.log(logger.EXTREME, "Beginning to send saved searches");
\r
755 for (int i=0; i<searches.size() && keepRunning; i++) {
\r
757 if (authRefreshNeeded)
\r
758 refreshConnection();
\r
760 SavedSearch enSearch = searches.get(i);
\r
762 if (enSearch.getUpdateSequenceNum() > 0)
\r
763 sequence = noteStore.updateSearch(authToken, enSearch);
\r
765 logger.log(logger.EXTREME, "New saved search found.");
\r
766 // Look for a tag with the same name. If one is found, we don't need
\r
767 // to create another one
\r
768 boolean found = false;
\r
769 logger.log(logger.EXTREME, "Matching remote saved search names with local");
\r
770 for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {
\r
771 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {
\r
772 enSearch = remoteList.get(k);
\r
774 logger.log(logger.EXTREME, "Matching saved search found");
\r
775 sequence = enSearch.getUpdateSequenceNum();
\r
779 String oldGuid = enSearch.getGuid();
\r
781 enSearch = noteStore.createSearch(authToken, enSearch);
\r
782 sequence = enSearch.getUpdateSequenceNum();
\r
783 logger.log(logger.EXTREME, "Updating tag guid in local database");
\r
784 conn.getTagTable().updateTagGuid(oldGuid, enSearch.getGuid());
\r
786 logger.log(logger.EXTREME, "Updating tag sequence in local database");
\r
787 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);
\r
788 logger.log(logger.EXTREME, "Resetting tag dirty flag");
\r
789 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());
\r
790 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");
\r
791 updateSequenceNumber = sequence;
\r
792 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
793 } catch (EDAMUserException e) {
\r
794 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");
\r
795 logger.log(logger.LOW, e.toString());
\r
797 } catch (EDAMSystemException e) {
\r
798 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");
\r
799 logger.log(logger.LOW, e.toString());
\r
801 } catch (EDAMNotFoundException e) {
\r
802 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");
\r
803 logger.log(logger.LOW, e.toString());
\r
805 } catch (TException e) {
\r
806 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");
\r
807 logger.log(logger.LOW, e.toString());
\r
811 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");
\r
814 // Sync evernote changes with local database
\r
815 private void syncRemoteToLocal() {
\r
816 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");
\r
818 List<Note> dirtyNotes = conn.getNoteTable().getDirty();
\r
819 dirtyNoteGuids = new Vector<String>();
\r
820 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {
\r
821 dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());
\r
824 int chunkSize = 10;
\r
825 SyncChunk chunk = null;
\r
826 boolean fullSync = false;
\r
827 boolean more = true;
\r
829 if (updateSequenceNumber == 0)
\r
832 status.message.emit("Downloading 0% complete.");
\r
834 while(more && keepRunning) {
\r
836 if (authRefreshNeeded)
\r
837 refreshConnection();
\r
840 int sequence = updateSequenceNumber;
\r
842 logger.log(logger.EXTREME, "Getting chunk from Evernote");
\r
843 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);
\r
844 } catch (EDAMUserException e) {
\r
846 e.printStackTrace();
\r
847 status.message.emit(e.getMessage());
\r
848 } catch (EDAMSystemException e) {
\r
850 e.printStackTrace();
\r
851 status.message.emit(e.getMessage());
\r
852 } catch (TException e) {
\r
854 e.printStackTrace();
\r
855 status.message.emit(e.getMessage());
\r
857 if (error || chunk == null)
\r
862 syncRemoteTags(chunk.getTags());
\r
863 syncRemoteSavedSearches(chunk.getSearches());
\r
864 syncRemoteNotebooks(chunk.getNotebooks());
\r
865 syncRemoteNotes(chunk.getNotes(), fullSync);
\r
866 syncRemoteResources(chunk.getResources());
\r
868 // Do the local deletes
\r
869 logger.log(logger.EXTREME, "Doing local deletes");
\r
870 List<String> guid = chunk.getExpungedNotes();
\r
872 for (int i=0; i<guid.size() && keepRunning; i++) {
\r
873 logger.log(logger.EXTREME, "Expunging local note from database");
\r
874 conn.getNoteTable().expungeNote(guid.get(i), true, false);
\r
876 guid = chunk.getExpungedNotebooks();
\r
878 for (int i=0; i<guid.size() && keepRunning; i++) {
\r
879 logger.log(logger.EXTREME, "Expunging local notebook from database");
\r
880 conn.getNotebookTable().expungeNotebook(guid.get(i), false);
\r
882 guid = chunk.getExpungedTags();
\r
884 for (int i=0; i<guid.size() && keepRunning; i++) {
\r
885 logger.log(logger.EXTREME, "Expunging tags from local database");
\r
886 conn.getTagTable().expungeTag(guid.get(i), false);
\r
888 guid = chunk.getExpungedSearches();
\r
890 for (int i=0; i<guid.size() && keepRunning; i++) {
\r
891 logger.log(logger.EXTREME, "Expunging saved search from local database");
\r
892 conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);
\r
896 // Check for more notes
\r
897 if (chunk.getChunkHighUSN() <= updateSequenceNumber)
\r
901 logger.log(logger.EXTREME, "More notes? " +more);
\r
904 // Save the chunk sequence number
\r
905 if (!error && chunk.getChunkHighUSN() > 0) {
\r
906 logger.log(logger.EXTREME, "emitting sequence number to main thread");
\r
907 updateSequenceNumber = chunk.getChunkHighUSN();
\r
908 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
\r
909 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);
\r
914 long pct = chunk.getChunkHighUSN() * 100;
\r
915 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());
\r
916 pct = pct/evernoteUpdateCount;
\r
917 status.message.emit("Downloading " +new Long(pct).toString()+"% complete.");
\r
921 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");
\r
923 // Sync remote tags
\r
924 private void syncRemoteTags(List<Tag> tags) {
\r
925 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");
\r
926 if (tags != null) {
\r
927 for (int i=0; i<tags.size() && keepRunning; i++) {
\r
929 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());
\r
930 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))
\r
931 conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());
\r
932 conn.getTagTable().syncTag(tags.get(i), false);
\r
935 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");
\r
937 // Sync remote tags
\r
938 private void syncRemoteSavedSearches(List<SavedSearch> searches) {
\r
939 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");
\r
940 if (searches != null) {
\r
941 for (int i=0; i<searches.size() && keepRunning; i++) {
\r
943 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());
\r
944 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))
\r
945 conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());
\r
946 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);
\r
949 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");
\r
951 // Sync remote Notebooks 2
\r
952 private void syncRemoteNotebooks(List<Notebook> notebooks) {
\r
953 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");
\r
954 if (notebooks != null) {
\r
955 for (int i=0; i<notebooks.size() && keepRunning; i++) {
\r
957 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());
\r
958 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))
\r
959 conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());
\r
960 conn.getNotebookTable().syncNotebook(notebooks.get(i), false);
\r
963 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");
\r
965 // Sync remote Resources
\r
966 private void syncRemoteResources(List<Resource> resource) {
\r
967 // This is how the logic for this works.
\r
968 // 1.) If the resource is not in the local database, we add it.
\r
969 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy
\r
970 // 3.) If a copy of the resource is in the local databbase and it is dirty and the hash doesn't match, we ignore it because there
\r
971 // is a conflict. The note conflict should get a copy of the resource at that time.
\r
973 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");
\r
974 if (resource != null) {
\r
975 for (int i=0; i<resource.size() && keepRunning; i++) {
\r
976 boolean saveNeeded = false;
\r
977 /* #1 */ Resource r = getEvernoteResource(resource.get(i).getGuid(), true,true,true);
\r
978 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);
\r
982 /* #2 */ boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());
\r
986 /* #3 */ String remoteHash = "";
\r
987 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)
\r
988 remoteHash = byteArrayToHexString(r.getData().getBodyHash());
\r
989 String localHash = "";
\r
990 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)
\r
991 remoteHash = byteArrayToHexString(l.getData().getBodyHash());
\r
993 if (localHash.equalsIgnoreCase(remoteHash))
\r
999 conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);
\r
1003 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");
\r
1005 // Sync remote notebooks
\r
1006 private void syncRemoteNotes(List<Note> note, boolean fullSync) {
\r
1007 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");
\r
1008 if (note != null) {
\r
1009 for (int i=0; i<note.size() && keepRunning; i++) {
\r
1010 Note n = getEvernoteNote(note.get(i).getGuid(), true, fullSync, true,true);
\r
1013 // Basically, this is how the sync logic for a note works.
\r
1014 // If the remote note has changed and the local has not, we
\r
1015 // accept the change.
\r
1016 // If both the local & remote have changed but the sequence
\r
1017 // numbers are the same, we don't accept the change. This
\r
1018 // seems to happen when attachments are indexed by the server.
\r
1019 // If both the local & remote have changed and the sequence numbers
\r
1020 // are different we move the local copy to a local notebook (making sure
\r
1021 // to copy all resources) and we accept the new one.
\r
1022 boolean conflictingNote = true;
\r
1023 logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid());
\r
1024 if (dirtyNoteGuids.contains(n.getGuid())) {
\r
1025 logger.log(logger.EXTREME, "Conflict check beginning");
\r
1026 conflictingNote = checkForConflict(n);
\r
1027 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);
\r
1028 if (conflictingNote)
\r
1029 moveConflictingNote(n.getGuid());
\r
1031 if (conflictingNote || fullSync) {
\r
1032 logger.log(logger.EXTREME, "Saving Note");
\r
1033 conn.getNoteTable().syncNote(n, false);
\r
1034 noteSignal.noteChanged.emit(n.getGuid(), null); // Signal to ivalidate note cache
\r
1035 logger.log(logger.EXTREME, "Note Saved");
\r
1036 if (fullSync && n.getResources() != null) {
\r
1037 for (int q=0; q<n.getResources().size() && keepRunning; q++) {
\r
1038 logger.log(logger.EXTREME, "Getting note resources.");
\r
1039 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);
\r
1046 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");
\r
1048 private Note getEvernoteNote(String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData) {
\r
1051 logger.log(logger.EXTREME, "Retrieving note " +guid);
\r
1052 n = noteStore.getNote(authToken, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);
\r
1053 logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");
\r
1054 } catch (EDAMUserException e) {
\r
1055 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
\r
1056 logger.log(logger.LOW, e.toString());
\r
1058 e.printStackTrace();
\r
1059 } catch (EDAMSystemException e) {
\r
1060 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
\r
1061 logger.log(logger.LOW, e.toString());
\r
1063 e.printStackTrace();
\r
1064 } catch (EDAMNotFoundException e) {
\r
1065 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
\r
1066 logger.log(logger.LOW, e.toString());
\r
1068 e.printStackTrace();
\r
1069 } catch (TException e) {
\r
1070 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
\r
1071 logger.log(logger.LOW, e.toString());
\r
1073 e.printStackTrace();
\r
1077 private Resource getEvernoteResource(String guid, boolean withData, boolean withRecognition, boolean withAttributes) {
\r
1078 Resource n = null;
\r
1080 logger.log(logger.EXTREME, "Retrieving resource " +guid);
\r
1081 n = noteStore.getResource(authToken, guid, withData, withRecognition, withAttributes, withAttributes);
\r
1082 logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");
\r
1083 } catch (EDAMUserException e) {
\r
1084 logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");
\r
1085 logger.log(logger.LOW, e.toString());
\r
1087 e.printStackTrace();
\r
1088 } catch (EDAMSystemException e) {
\r
1089 logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");
\r
1090 logger.log(logger.LOW, e.toString());
\r
1092 e.printStackTrace();
\r
1093 } catch (EDAMNotFoundException e) {
\r
1094 logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");
\r
1095 logger.log(logger.LOW, e.toString());
\r
1097 e.printStackTrace();
\r
1098 } catch (TException e) {
\r
1099 logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");
\r
1100 logger.log(logger.LOW, e.toString());
\r
1102 e.printStackTrace();
\r
1108 private boolean checkForConflict(Note n) {
\r
1109 logger.log(logger.EXTREME, "Checking note sequence number " +n.getGuid());
\r
1110 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);
\r
1111 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());
\r
1112 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum())
\r
1117 private void moveConflictingNote(String guid) {
\r
1118 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);
\r
1119 List<Notebook> books = conn.getNotebookTable().getAllLocal();
\r
1120 String notebookGuid = null;
\r
1121 for (int i=0; i<books.size() && keepRunning; i++) {
\r
1122 if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||
\r
1123 books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {
\r
1124 notebookGuid = books.get(i).getGuid();
\r
1129 if (notebookGuid == null) {
\r
1130 logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);
\r
1131 Calendar currentTime = new GregorianCalendar();
\r
1132 Long l = new Long(currentTime.getTimeInMillis());
\r
1133 long prevTime = l;
\r
1134 while (prevTime==l) {
\r
1135 currentTime = new GregorianCalendar();
\r
1136 l=currentTime.getTimeInMillis();
\r
1138 String randint = new String(Long.toString(l));
\r
1140 Notebook newBook = new Notebook();
\r
1141 newBook.setUpdateSequenceNum(0);
\r
1142 newBook.setGuid(randint);
\r
1143 newBook.setName("Conflicting Changes");
\r
1144 newBook.setServiceCreated(new Date().getTime());
\r
1145 newBook.setServiceUpdated(new Date().getTime());
\r
1146 newBook.setDefaultNotebook(false);
\r
1147 newBook.setPublished(false);
\r
1149 conn.getNotebookTable().addNotebook(newBook, false, true);
\r
1150 notebookGuid = newBook.getGuid();
\r
1153 // Now that we have a good notebook guid, we need to move the conflicting note
\r
1154 // to the local notebook
\r
1155 logger.log(logger.EXTREME, "Moving conflicting note " +guid);
\r
1156 Calendar currentTime = new GregorianCalendar();
\r
1157 Long l = new Long(currentTime.getTimeInMillis());
\r
1158 long prevTime = l;
\r
1159 while (prevTime==l) {
\r
1160 currentTime = new GregorianCalendar();
\r
1161 l = currentTime.getTimeInMillis();
\r
1163 String newGuid = new String(Long.toString(l));
\r
1165 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);
\r
1166 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {
\r
1167 l = new Long(currentTime.getTimeInMillis());
\r
1168 String newResG = new String(Long.toString(l));
\r
1169 String oldResG = oldNote.getResources().get(i).getGuid();
\r
1170 conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);
\r
1171 conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);
\r
1174 conn.getNoteTable().resetSequenceNumber(guid);
\r
1175 conn.getNoteTable().updateNoteGuid(guid, newGuid);
\r
1176 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);
\r
1179 noteSignal.guidChanged.emit(guid,newGuid);
\r
1185 //******************************************************
\r
1186 //******************************************************
\r
1187 //** Utility Functions
\r
1188 //******************************************************
\r
1189 //******************************************************
\r
1190 // Convert a byte array to a hex string
\r
1191 private static String byteArrayToHexString(byte data[]) {
\r
1192 StringBuffer buf = new StringBuffer();
\r
1193 for (byte element : data) {
\r
1194 int halfbyte = (element >>> 4) & 0x0F;
\r
1195 int two_halfs = 0;
\r
1197 if ((0 <= halfbyte) && (halfbyte <= 9))
\r
1198 buf.append((char) ('0' + halfbyte));
\r
1200 buf.append((char) ('a' + (halfbyte - 10)));
\r
1201 halfbyte = element & 0x0F;
\r
1202 } while(two_halfs++ < 1);
\r
1204 return buf.toString();
\r
1209 //*******************************************************
\r
1210 //* Find dirty tags, which do not have newly created parents
\r
1211 //*******************************************************
\r
1212 private Tag findNextTag() {
\r
1213 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");
\r
1214 Tag nextTag = null;
\r
1215 List<Tag> tags = conn.getTagTable().getDirty();
\r
1217 // Find the parent. If the parent has a sequence > 0 then it is a good
\r
1219 for (int i=0; i<tags.size() && keepRunning; i++) {
\r
1220 if (tags.get(i).getParentGuid() == null) {
\r
1221 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");
\r
1222 return tags.get(i);
\r
1224 Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());
\r
1225 if (parentTag.getUpdateSequenceNum() > 0) {
\r
1226 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");
\r
1227 return tags.get(i);
\r
1231 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");
\r
1236 // Connect to Evernote
\r
1237 public boolean enConnect() {
\r
1239 userStoreTrans = new THttpClient(userStoreUrl);
\r
1240 } catch (TTransportException e) {
\r
1241 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
\r
1243 e.printStackTrace();
\r
1245 userStoreProt = new TBinaryProtocol(userStoreTrans);
\r
1246 userStore = new UserStore.Client(userStoreProt, userStoreProt);
\r
1247 syncSignal.saveUserStore.emit(userStore);
\r
1249 authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);
\r
1250 } catch (EDAMUserException e) {
\r
1251 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Incorrect username/password");
\r
1253 isConnected = false;
\r
1255 } catch (EDAMSystemException e) {
\r
1256 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());
\r
1258 e.printStackTrace();
\r
1259 isConnected = false;
\r
1260 } catch (TException e) {
\r
1261 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
\r
1263 e.printStackTrace();
\r
1264 isConnected = false;
\r
1267 boolean versionOk = false;
\r
1269 // versionOk = userStore.checkVersion("Dave's EDAMDemo (Java)",
\r
1270 versionOk = userStore.checkVersion("NeverNote",
\r
1271 com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR,
\r
1272 com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);
\r
1273 } catch (TException e) {
\r
1274 e.printStackTrace();
\r
1275 isConnected = false;
\r
1277 if (!versionOk) {
\r
1278 System.err.println("Incomatible EDAM client protocol version");
\r
1279 isConnected = false;
\r
1281 if (authResult != null) {
\r
1282 user = authResult.getUser();
\r
1283 authToken = authResult.getAuthenticationToken();
\r
1284 noteStoreUrl = noteStoreUrlBase + user.getShardId();
\r
1285 syncSignal.saveAuthToken.emit(authToken);
\r
1286 syncSignal.saveNoteStore.emit(noteStore);
\r
1290 noteStoreTrans = new THttpClient(noteStoreUrl);
\r
1291 } catch (TTransportException e) {
\r
1292 QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());
\r
1294 e.printStackTrace();
\r
1295 isConnected = false;
\r
1297 noteStoreProt = new TBinaryProtocol(noteStoreTrans);
\r
1299 new NoteStore.Client(noteStoreProt, noteStoreProt);
\r
1300 isConnected = true;
\r
1301 authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
\r
1302 authRefreshTime = authTimeRemaining / 2;
\r
1305 // Get user information
\r
1307 User user = userStore.getUser(authToken);
\r
1308 syncSignal.saveUserInformation.emit(user);
\r
1309 } catch (EDAMUserException e1) {
\r
1310 e1.printStackTrace();
\r
1311 } catch (EDAMSystemException e1) {
\r
1312 e1.printStackTrace();
\r
1313 } catch (TException e1) {
\r
1314 e1.printStackTrace();
\r
1317 return isConnected;
\r
1319 // Disconnect from the database
\r
1320 public void enDisconnect() {
\r
1321 isConnected = false;
\r
1323 // Refresh the connection
\r
1324 private synchronized void refreshConnection() {
\r
1325 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");
\r
1326 // Calendar cal = Calendar.getInstance();
\r
1328 // If we are not connected let's get out of here
\r
1332 // If we fail too many times, then let's give up.
\r
1333 if (failedRefreshes >=5) {
\r
1334 logger.log(logger.EXTREME, "Refresh attempts have failed. Disconnecting.");
\r
1335 isConnected = false;
\r
1339 // If this is the first time through, then we need to set this
\r
1340 // if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime)
\r
1341 // authRefreshTime = cal.getTimeInMillis();
\r
1343 // // Default to checking again in 5 min. This in case we fail.
\r
1344 // authRefreshTime = authRefreshTime +(5*60*1000);
\r
1346 // Try to get a new token
\r
1347 AuthenticationResult newAuth = null;
\r
1348 logger.log(logger.EXTREME, "Beginning to try authentication refresh");
\r
1350 if (userStore != null && authToken != null)
\r
1351 newAuth = userStore.refreshAuthentication(authToken);
\r
1354 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");
\r
1355 } catch (EDAMUserException e) {
\r
1356 e.printStackTrace();
\r
1357 syncSignal.authRefreshComplete.emit(false);
\r
1358 failedRefreshes++;
\r
1360 } catch (EDAMSystemException e) {
\r
1361 e.printStackTrace();
\r
1362 syncSignal.authRefreshComplete.emit(false);
\r
1363 failedRefreshes++;
\r
1365 } catch (TException e) {
\r
1366 e.printStackTrace();
\r
1367 syncSignal.authRefreshComplete.emit(false);
\r
1368 failedRefreshes++;
\r
1372 // If we didn't get a good auth, then we've failed
\r
1373 if (newAuth == null) {
\r
1374 failedRefreshes++;
\r
1375 logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);
\r
1376 syncSignal.authRefreshComplete.emit(false);
\r
1380 // We got a good token. Now we need to setup the time to renew it.
\r
1381 logger.log(logger.EXTREME, "Saving authentication tokens");
\r
1382 authResult = newAuth;
\r
1383 authToken = new String(newAuth.getAuthenticationToken());
\r
1384 // authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();
\r
1385 // authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);
\r
1386 failedRefreshes=0;
\r
1387 syncSignal.authRefreshComplete.emit(true);
\r
1388 authRefreshNeeded = false;
\r
1390 // This should never happen, but if it does we consider this a faild attempt.
\r
1391 // if (authTimeRemaining <= 0) {
\r
1392 // failedRefreshes++;
\r
1393 // syncSignal.authRefreshComplete.emit(false);
\r
1397 public synchronized boolean addWork(String request) {
\r
1398 if (workQueue.offer(request))
\r
1403 private Note getNoteContent(Note n) {
\r
1404 n.setContent(conn.getNoteTable().getNoteContentBinary(n.getGuid()));
\r