OSDN Git Service

There are multiple changes with this commit.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / threads / SyncRunner.java
1 /*\r
2  * This file is part of NeverNote \r
3  * Copyright 2009 Randy Baumgarte\r
4  * \r
5  * This file may be licensed under the terms of of the\r
6  * GNU General Public License Version 2 (the ``GPL'').\r
7  *\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
12  *\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
17  *\r
18 */\r
19 package cx.fbn.nevernote.threads;\r
20 \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
29 \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
34 \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
52 \r
53 import cx.fbn.nevernote.signals.NoteIndexSignal;\r
54 import cx.fbn.nevernote.signals.NoteResourceSignal;\r
55 import cx.fbn.nevernote.signals.NoteSignal;\r
56 import cx.fbn.nevernote.signals.NotebookSignal;\r
57 import cx.fbn.nevernote.signals.SavedSearchSignal;\r
58 import cx.fbn.nevernote.signals.StatusSignal;\r
59 import cx.fbn.nevernote.signals.SyncSignal;\r
60 import cx.fbn.nevernote.signals.TagSignal;\r
61 import cx.fbn.nevernote.sql.DatabaseConnection;\r
62 import cx.fbn.nevernote.sql.DeletedItemRecord;\r
63 import cx.fbn.nevernote.utilities.ApplicationLogger;\r
64 \r
65 public class SyncRunner extends QObject implements Runnable {\r
66         \r
67         private final ApplicationLogger logger;\r
68                 private DatabaseConnection              conn;\r
69                 private boolean                                 idle;\r
70                 private boolean                                 error;\r
71                 public volatile boolean                 isConnected;\r
72                 public volatile boolean                 keepRunning;\r
73                 public volatile String                  authToken;\r
74                 private long                                    evernoteUpdateCount;\r
75                 \r
76                 public volatile NoteStore.Client                noteStore;\r
77                 private UserStore.Client                                userStore;\r
78                 \r
79                 public volatile StatusSignal                    status;\r
80                 public volatile TagSignal                               tagSignal;\r
81                 public volatile NotebookSignal                  notebookSignal;\r
82                 public volatile NoteIndexSignal                 noteIndexSignal;\r
83                 public volatile NoteSignal                              noteSignal;\r
84                 public volatile SavedSearchSignal               searchSignal;\r
85                 public volatile NoteResourceSignal              resourceSignal;\r
86                 public volatile SyncSignal                              syncSignal;\r
87                 public volatile boolean                                 authRefreshNeeded;\r
88                 public volatile boolean                                 syncNeeded;\r
89                 public volatile boolean                                 disableUploads;\r
90                 public volatile boolean                                 syncDeletedContent;\r
91                 private volatile Vector<String>                 dirtyNoteGuids;\r
92                 \r
93             public volatile String username = ""; \r
94             public volatile String password = ""; \r
95                 public volatile String userStoreUrl;\r
96             private final static String consumerKey = "baumgarte"; \r
97             private final static String consumerSecret = "eb8b5740e17cb55f";\r
98             public String noteStoreUrlBase;\r
99             private THttpClient userStoreTrans;\r
100             private TBinaryProtocol userStoreProt;\r
101             private AuthenticationResult authResult;\r
102             private User user; \r
103             private long authTimeRemaining;\r
104             public long authRefreshTime;\r
105             public long failedRefreshes = 0;\r
106             public  THttpClient noteStoreTrans;\r
107             public TBinaryProtocol noteStoreProt;\r
108             public String noteStoreUrl;\r
109             public long sequenceDate;\r
110             public int updateSequenceNumber;\r
111             private boolean refreshNeeded;\r
112             private volatile LinkedBlockingQueue<String> workQueue;\r
113 //              private static int MAX_EMPTY_QUEUE_COUNT = 1;\r
114                 private static int MAX_QUEUED_WAITING = 1000;\r
115                 String dbuid;\r
116                 String dburl;\r
117                 String dbpswd;\r
118                 String dbcpswd;\r
119         \r
120                 \r
121                 \r
122         public SyncRunner(String logname, String u, String uid, String pswd, String cpswd) {\r
123                 logger = new ApplicationLogger(logname);\r
124                 \r
125                 noteSignal = new NoteSignal();\r
126                 status = new StatusSignal();\r
127                 tagSignal = new TagSignal();\r
128                 notebookSignal = new NotebookSignal();\r
129                 noteIndexSignal = new NoteIndexSignal();\r
130                 noteSignal = new NoteSignal();\r
131                 searchSignal = new SavedSearchSignal();\r
132                 syncSignal = new SyncSignal();\r
133                 resourceSignal = new NoteResourceSignal();\r
134                 dbuid = uid;\r
135                 dburl = u;\r
136                 dbpswd = pswd;\r
137                 dbcpswd = cpswd;\r
138 //              this.setAutoDelete(false);\r
139                 \r
140                 isConnected = false;\r
141                 syncNeeded = false;\r
142                 authRefreshNeeded = false;\r
143                 keepRunning = true;\r
144                 idle = true;\r
145                 noteStore = null;\r
146                 userStore = null;\r
147                 authToken = null;\r
148                 disableUploads = false;\r
149 //              setAutoDelete(false);\r
150                 workQueue=new LinkedBlockingQueue<String>(MAX_QUEUED_WAITING);\r
151         }\r
152         @Override\r
153         public void run() {\r
154                 try {\r
155                         logger.log(logger.EXTREME, "Starting thread");\r
156                         conn = new DatabaseConnection(logger, dburl, dbuid, dbpswd, dbcpswd);\r
157                         while(keepRunning) {\r
158                                 String work = workQueue.take();\r
159                                 logger.log(logger.EXTREME, "Work found: " +work);\r
160                                 if (work.equalsIgnoreCase("stop"))\r
161                                         return;\r
162                                 idle=false;\r
163                                 error=false;\r
164                                 if (authRefreshNeeded == true) {\r
165                                         logger.log(logger.EXTREME, "Refreshing connection");\r
166                                         refreshConnection();\r
167                                 }\r
168                                 if (syncNeeded) {\r
169                                         logger.log(logger.EXTREME, "SyncNeeded is true");\r
170                                         refreshNeeded=false;\r
171                                         sequenceDate = conn.getSyncTable().getLastSequenceDate();\r
172                                         updateSequenceNumber = conn.getSyncTable().getUpdateSequenceNumber();\r
173                                         try {\r
174                                                 logger.log(logger.EXTREME, "Beginning sync");\r
175                                                 evernoteSync();\r
176                                                 logger.log(logger.EXTREME, "Sync finished");\r
177                                         } catch (UnknownHostException e) {\r
178                                                 status.message.emit(e.getMessage());\r
179                                         }\r
180                                 }\r
181                                 dirtyNoteGuids = null;\r
182                                 idle=true;\r
183                                 logger.log(logger.EXTREME, "Signaling refresh finished.  refreshNeeded=" +refreshNeeded);\r
184                                 syncSignal.finished.emit(refreshNeeded);\r
185                         }\r
186                 }       \r
187                 catch (InterruptedException e1) {\r
188                         e1.printStackTrace();\r
189                 }\r
190                 conn.dbShutdown();\r
191         }\r
192 \r
193         \r
194         public DatabaseConnection getConnection() {\r
195                 return conn;\r
196         }\r
197 \r
198         public boolean isIdle() {\r
199                 return idle;\r
200         }\r
201 \r
202 \r
203         public void setConnected(boolean c) {\r
204                 isConnected = c;\r
205         }\r
206         public void setKeepRunning(boolean r) {\r
207                 logger.log(logger.EXTREME, "Setting keepRunning=" +r);\r
208                 keepRunning = r;\r
209         }\r
210         public void setNoteStore(NoteStore.Client c) {\r
211                 logger.log(logger.EXTREME, "Setting NoteStore in sync thread");\r
212                 noteStore = c;\r
213         }\r
214         public void setUserStore(UserStore.Client c) {\r
215                 logger.log(logger.EXTREME, "Setting UserStore in sync thread");\r
216                 userStore = c;\r
217         }\r
218 \r
219         public void setEvernoteUpdateCount(long s) {\r
220                 logger.log(logger.EXTREME, "Setting Update Count in sync thread");\r
221                 evernoteUpdateCount = s;\r
222         }\r
223         \r
224         //***************************************************************\r
225     //***************************************************************\r
226     //** These functions deal with Evernote communications\r
227     //***************************************************************\r
228     //***************************************************************\r
229         // Synchronize changes with Evernote\r
230         @SuppressWarnings("unused")\r
231         private void evernoteSync() throws java.net.UnknownHostException {\r
232                 logger.log(logger.HIGH, "Entering SyncRunner.evernoteSync");\r
233 \r
234                 if (isConnected && keepRunning) {\r
235                         error = false;\r
236                         logger.log(logger.EXTREME, "Synchronizing with Evernote");\r
237                         status.message.emit("Synchronizing with Evernote");\r
238                         \r
239                         // Get user information\r
240                         try {\r
241                                 logger.log(logger.EXTREME, "getting user from userstore");\r
242                                 User user = userStore.getUser(authToken);\r
243                                 logger.log(logger.EXTREME, "Saving user information");\r
244                                 syncSignal.saveUserInformation.emit(user);\r
245                         } catch (EDAMUserException e1) {\r
246                                 e1.printStackTrace();\r
247                                 status.message.emit("User exception getting user account information.  Aborting sync and disconnecting");\r
248                                 syncSignal.errorDisconnect.emit();\r
249                                 enDisconnect();\r
250                                 return;\r
251                         } catch (EDAMSystemException e1) {\r
252                                 e1.printStackTrace();\r
253                                 status.message.emit("System error user account information.  Aborting sync and disconnecting!");\r
254                                 syncSignal.errorDisconnect.emit();\r
255                                 enDisconnect();\r
256                                 return;\r
257                         } catch (TException e1) {\r
258                                 e1.printStackTrace();\r
259                                 syncSignal.errorDisconnect.emit();\r
260                                 status.message.emit("Transaction error getting user account information.  Aborting sync and disconnecting!");\r
261                                 enDisconnect();\r
262                                 return;\r
263                         }\r
264                         \r
265                         // Get sync state\r
266                         SyncState syncState = null;\r
267                         try {   \r
268                                 logger.log(logger.EXTREME, "Getting sync state");\r
269                                 syncState = noteStore.getSyncState(authToken);  \r
270                                 syncSignal.saveUploadAmount.emit(syncState.getUploaded());\r
271                                 syncSignal.saveEvernoteUpdateCount.emit(syncState.getUpdateCount());\r
272                                 evernoteUpdateCount = syncState.getUpdateCount();\r
273                         } catch (EDAMUserException e) {\r
274                                 e.printStackTrace();\r
275                                 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
276                                 syncSignal.errorDisconnect.emit();\r
277                                 enDisconnect();\r
278                                 return;\r
279                         } catch (EDAMSystemException e) {\r
280                                 e.printStackTrace();\r
281                                 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
282                                 syncSignal.errorDisconnect.emit();\r
283                                 enDisconnect();\r
284                                 return;\r
285                         } catch (TException e) {\r
286                                 e.printStackTrace();\r
287                                 status.message.emit("Error getting sync state! Aborting sync and disconnecting!");\r
288                                 syncSignal.errorDisconnect.emit();\r
289                                 enDisconnect();\r
290                                 return;\r
291                         }\r
292                         \r
293                         if (syncState == null) {\r
294                                 logger.log(logger.EXTREME, "Sync State is null");\r
295                                 status.message.emit("Syncronization Error!");\r
296                                 return;\r
297                         }\r
298 \r
299                         // Determine what to do. \r
300                         // If we need to do a full sync.\r
301                         logger.log(logger.LOW, "Full Sequence Before: " +syncState.getFullSyncBefore());\r
302                         logger.log(logger.LOW, "Last Sequence Date: " +sequenceDate);\r
303                         if (syncState.getFullSyncBefore() > sequenceDate) {\r
304                                 logger.log(logger.EXTREME, "Full sequence date has expired");\r
305                                 sequenceDate = 0;\r
306                                 conn.getSyncTable().setLastSequenceDate(0);\r
307                                 updateSequenceNumber = 0;\r
308                                 conn.getSyncTable().setUpdateSequenceNumber(0);\r
309                         }\r
310 \r
311                         // If there are remote changes\r
312                         logger.log(logger.LOW, "Update Count: " +syncState.getUpdateCount());\r
313                         logger.log(logger.LOW, "Last Update Count: " +updateSequenceNumber);\r
314                         \r
315                         if (syncState.getUpdateCount() > updateSequenceNumber) {\r
316                                 logger.log(logger.EXTREME, "Refresh needed is true");\r
317                                 refreshNeeded = true;\r
318                                 logger.log(logger.EXTREME, "Downloading changes");\r
319                                 syncRemoteToLocal();\r
320                         }\r
321                         \r
322                         if (!disableUploads) {\r
323                                 logger.log(logger.EXTREME, "Uploading changes");\r
324                                 // Synchronize remote changes\r
325                                 if (!error)\r
326                                         syncExpunged();\r
327                                 if (!error)\r
328                                         syncLocalTags();\r
329                                 if (!error)\r
330                                         syncLocalNotebooks();\r
331                                 if (!error) \r
332                                         syncDeletedNotes();\r
333                                 if (!error)\r
334                                         syncLocalNotes();\r
335                                 if (!error)\r
336                                         syncLocalSavedSearches();\r
337                         }\r
338                         if (refreshNeeded)\r
339                                 syncSignal.refreshLists.emit();\r
340                         if (!error) {\r
341                                 logger.log(logger.EXTREME, "Sync completed.  Errors=" +error);\r
342                                 if (!disableUploads)\r
343                                         status.message.emit("Synchronizing complete");\r
344                                 else\r
345                                         status.message.emit("Download syncronization complete.  Uploads have been disabled.");\r
346                                 \r
347                                 logger.log(logger.EXTREME, "Saving sync time");\r
348                                 if (syncState.getCurrentTime() > sequenceDate)\r
349                                         sequenceDate = syncState.getCurrentTime();\r
350                                 if (syncState.getUpdateCount() > updateSequenceNumber)\r
351                                         updateSequenceNumber = syncState.getUpdateCount();\r
352                                 conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
353                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
354                         }\r
355                 }\r
356                 logger.log(logger.HIGH, "Leaving SyncRunner.evernoteSync");\r
357         }\r
358         // Sync deleted items with Evernote\r
359         private void syncExpunged() {\r
360                 logger.log(logger.HIGH, "Entering SyncRunner.syncExpunged");\r
361                 \r
362                 List<DeletedItemRecord> expunged = conn.getDeletedTable().getAllDeleted();\r
363                 boolean error = false;\r
364                 for (int i=0; i<expunged.size(); i++) {\r
365                         \r
366                         if (authRefreshNeeded)\r
367                                 refreshConnection();\r
368 \r
369                         try {\r
370                                 if (expunged.get(i).type.equalsIgnoreCase("TAG")) {\r
371                                         logger.log(logger.EXTREME, "Tag expunged");\r
372                                         updateSequenceNumber = noteStore.expungeTag(authToken, expunged.get(i).guid);\r
373                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
374                                         \r
375                                 }\r
376                                 if      (expunged.get(i).type.equalsIgnoreCase("NOTEBOOK")) {\r
377                                         logger.log(logger.EXTREME, "Notebook expunged");\r
378                                         updateSequenceNumber = noteStore.expungeNotebook(authToken, expunged.get(i).guid);\r
379                                         conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
380                                 }\r
381                                 if (expunged.get(i).type.equalsIgnoreCase("NOTE")) {\r
382                                         logger.log(logger.EXTREME, "Note expunged");\r
383                                         updateSequenceNumber = noteStore.deleteNote(authToken, expunged.get(i).guid);\r
384                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
385                                 }\r
386                                 if (expunged.get(i).type.equalsIgnoreCase("SAVEDSEARCH")) {\r
387                                         logger.log(logger.EXTREME, "saved search expunged");\r
388                                         updateSequenceNumber = noteStore.expungeSearch(authToken, expunged.get(i).guid);\r
389                                         conn.getSyncTable().setLastSequenceDate(sequenceDate);\r
390                                 }\r
391                         } catch (EDAMUserException e) {\r
392                                 logger.log(logger.LOW, "EDAM User Excepton in syncExpunged: " +expunged.get(i).guid);\r
393                                 logger.log(logger.LOW, e.getStackTrace());\r
394                                 error = true;\r
395                         } catch (EDAMSystemException e) {\r
396                                 logger.log(logger.LOW, "EDAM System Excepton in syncExpunged: "+expunged.get(i).guid);\r
397                                 logger.log(logger.LOW, e.getStackTrace());\r
398                                 error=true;\r
399                         } catch (EDAMNotFoundException e) {\r
400                                 logger.log(logger.LOW, "EDAM Not Found Excepton in syncExpunged: "+expunged.get(i).guid);\r
401 //                              logger.log(logger.LOW, e.getStackTrace());\r
402                                 //error=true;\r
403                         } catch (TException e) {\r
404                                 logger.log(logger.LOW, "EDAM TExcepton in syncExpunged: "+expunged.get(i).guid);\r
405                                 logger.log(logger.LOW, e.getStackTrace());\r
406                                 error=true;\r
407                         }\r
408                 }\r
409                 if (!error)\r
410                         conn.getDeletedTable().expungeAllDeletedRecords();\r
411                 \r
412                 logger.log(logger.HIGH, "Leaving SyncRunner.syncExpunged");\r
413 \r
414         }\r
415         private void syncDeletedNotes() {\r
416                 if (syncDeletedContent)\r
417                         return;\r
418                 logger.log(logger.HIGH, "Entering SyncRunner.syncDeletedNotes");\r
419                 status.message.emit("Synchronizing deleted notes.");\r
420 \r
421                 List<Note> notes = conn.getNoteTable().getDirty();\r
422                 // Sync the local notebooks with Evernote's\r
423                 for (int i=0; i<notes.size() && keepRunning; i++) {\r
424                         \r
425                         if (authRefreshNeeded)\r
426                                 refreshConnection();\r
427                         \r
428                         Note enNote = notes.get(i);\r
429                         try {\r
430                                 if (enNote.getUpdateSequenceNum() > 0 && (enNote.isActive() == false || enNote.getDeleted() > 0)) {\r
431                                         if (syncDeletedContent) {\r
432                                                 logger.log(logger.EXTREME, "Deleted note found & synch content selected");\r
433                                                 Note delNote = conn.getNoteTable().getNote(enNote.getGuid(), true, true, true, true, true);\r
434                                                 delNote = getNoteContent(delNote);\r
435                                                 delNote = noteStore.updateNote(authToken, delNote);\r
436                                                 enNote.setUpdateSequenceNum(delNote.getUpdateSequenceNum());\r
437                                                 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
438                                         } else {\r
439                                                 logger.log(logger.EXTREME, "Deleted note found & sync content not selected");\r
440                                                 int usn = noteStore.deleteNote(authToken, enNote.getGuid());\r
441                                                 enNote.setUpdateSequenceNum(usn);\r
442                                                 conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());                                                \r
443                                         }\r
444                                         logger.log(logger.EXTREME, "Resetting deleted dirty flag");\r
445                                         conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
446                                         updateSequenceNumber = enNote.getUpdateSequenceNum();\r
447                                         logger.log(logger.EXTREME, "Saving sequence number");\r
448                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
449                                 }                               \r
450                         } catch (EDAMUserException e) {\r
451                                 //logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
452                                 //status.message.emit("Error sending local note: " +e.getParameter());\r
453                                 //logger.log(logger.LOW, e.toString()); \r
454                                 //error = true;\r
455                         } catch (EDAMSystemException e) {\r
456                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
457                                 status.message.emit("Error: " +e);\r
458                                 logger.log(logger.LOW, e.toString());           \r
459                                 error = true;\r
460                         } catch (EDAMNotFoundException e) {\r
461                                 //logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
462                                 //status.message.emit("Error deleting local note: " +e +" - Continuing");\r
463                                 //logger.log(logger.LOW, e.toString());         \r
464                                 //error = true;\r
465                         } catch (TException e) {\r
466                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
467                                 status.message.emit("Error sending local note: " +e);\r
468                                 logger.log(logger.LOW, e.toString());   \r
469                                 error = true;\r
470                         }               \r
471                 }\r
472         }\r
473         // Sync notes with Evernote\r
474         private void syncLocalNotes() {\r
475                 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
476                 status.message.emit("Sending local notes.");\r
477 \r
478                 List<Note> notes = conn.getNoteTable().getDirty();\r
479                 // Sync the local notebooks with Evernote's\r
480                 for (int i=0; i<notes.size() && keepRunning; i++) {\r
481                         \r
482                         if (authRefreshNeeded)\r
483                                 refreshConnection();\r
484                         \r
485                         Note enNote = notes.get(i);\r
486                         if (enNote.isActive()) {\r
487                                 try {\r
488                                         logger.log(logger.EXTREME, "Active dirty note found - non new");\r
489                                         if (enNote.getUpdateSequenceNum() > 0) {\r
490                                                 enNote = getNoteContent(enNote);\r
491                                                 System.out.println("--------");\r
492                                                 System.out.println("Note:" +enNote);\r
493                                                 System.out.println("--------");\r
494                                                 logger.log(logger.MEDIUM, "Updating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
495                                                 enNote = noteStore.updateNote(authToken, enNote);\r
496                                         } else { \r
497                                                 logger.log(logger.EXTREME, "Active dirty found - new note");\r
498                                                 logger.log(logger.MEDIUM, "Creating note : "+ enNote.getGuid() +" <title>" +enNote.getTitle()+"</title>");\r
499                                                 String oldGuid = enNote.getGuid();\r
500                                                 enNote = getNoteContent(enNote);\r
501                                                 enNote = noteStore.createNote(authToken, enNote);\r
502                                                 noteSignal.guidChanged.emit(oldGuid, enNote.getGuid());\r
503                                                 conn.getNoteTable().updateNoteGuid(oldGuid, enNote.getGuid());\r
504                                         }\r
505                                         updateSequenceNumber = enNote.getUpdateSequenceNum();\r
506                                         logger.log(logger.EXTREME, "Saving note");\r
507                                         conn.getNoteTable().updateNoteSequence(enNote.getGuid(), enNote.getUpdateSequenceNum());\r
508                                         List<Resource> rl = enNote.getResources();\r
509                                         logger.log(logger.EXTREME, "Getting note resources");\r
510                                         for (int j=0; j<enNote.getResourcesSize() && keepRunning; j++) {\r
511                                                 Resource newRes = rl.get(j);\r
512                                                 Data d = newRes.getData();\r
513                                                 if (d!=null) {  \r
514                                                         logger.log(logger.EXTREME, "Calculating resource hash");\r
515                                                         String hash = byteArrayToHexString(d.getBodyHash());\r
516                                                         logger.log(logger.EXTREME, "updating resources by hash");\r
517                                                         String oldGuid = conn.getNoteTable().noteResourceTable.getNoteResourceGuidByHashHex(enNote.getGuid(), hash);\r
518                                                         conn.getNoteTable().updateNoteResourceGuidbyHash(enNote.getGuid(), newRes.getGuid(), hash);\r
519                                                         resourceSignal.resourceGuidChanged.emit(enNote.getGuid(), oldGuid, newRes.getGuid());\r
520                                                 }\r
521                                         }\r
522                                         logger.log(logger.EXTREME, "Resetting note dirty flag");\r
523                                         conn.getNoteTable().resetDirtyFlag(enNote.getGuid());\r
524                                         updateSequenceNumber = enNote.getUpdateSequenceNum();\r
525                                         logger.log(logger.EXTREME, "Emitting note sequence number change");\r
526                                         conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
527 \r
528                                 } catch (EDAMUserException e) {\r
529                                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotes "+e);\r
530                                         status.message.emit("Error sending local note: " +e.getParameter());\r
531                                         logger.log(logger.LOW, e.toString());   \r
532                                         error = true;\r
533                                 } catch (EDAMSystemException e) {\r
534                                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotes "+e);\r
535                                         status.message.emit("Error: " +e);\r
536                                         logger.log(logger.LOW, e.toString());           \r
537                                         error = true;\r
538                                 } catch (EDAMNotFoundException e) {\r
539                                         logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotes " +e);\r
540                                         status.message.emit("Error sending local note: " +e);\r
541                                         logger.log(logger.LOW, e.toString());   \r
542                                         error = true;\r
543                                 } catch (TException e) {\r
544                                         logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotes "+e);\r
545                                         status.message.emit("Error sending local note: " +e);\r
546                                         logger.log(logger.LOW, e.toString());   \r
547                                         error = true;\r
548                                 }\r
549                         }\r
550                 }\r
551                 logger.log(logger.HIGH, "Entering SyncRunner.syncNotes");\r
552 \r
553         }\r
554         // Sync Notebooks with Evernote\r
555         private void syncLocalNotebooks() {\r
556                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalNotebooks");\r
557                 \r
558                 status.message.emit("Sending local notebooks.");\r
559                 List<Notebook> remoteList = new ArrayList<Notebook>();\r
560                 try {\r
561                         logger.log(logger.EXTREME, "Getting remote notebooks to compare with local");\r
562                         remoteList = noteStore.listNotebooks(authToken);\r
563                 } catch (EDAMUserException e1) {\r
564                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks getting remote Notebook List");\r
565                         status.message.emit("Error: " +e1);\r
566                         logger.log(logger.LOW, e1.toString());          \r
567                         error = true;\r
568                 } catch (EDAMSystemException e1) {\r
569                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks getting remote Notebook List");\r
570                         status.message.emit("Error: " +e1);\r
571                         logger.log(logger.LOW, e1.toString());  \r
572                         error = true;\r
573                 } catch (TException e1) {\r
574                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalNotebooks getting remote Notebook List");\r
575                         status.message.emit("Error: " +e1);\r
576                         logger.log(logger.LOW, e1.toString());  \r
577                         error = true;\r
578                 }\r
579                 logger.log(logger.EXTREME, "Getting local dirty notebooks");\r
580                 List<Notebook> notebooks = conn.getNotebookTable().getDirty();\r
581                 int sequence;\r
582                 // Sync the local notebooks with Evernote's\r
583                 for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
584                         \r
585                         if (authRefreshNeeded)\r
586                                 refreshConnection();\r
587                         \r
588                         Notebook enNotebook = notebooks.get(i);\r
589                         try {\r
590                                 if (enNotebook.getUpdateSequenceNum() > 0) {\r
591                                         logger.log(logger.EXTREME, "Existing notebook is dirty");\r
592                                         sequence = noteStore.updateNotebook(authToken, enNotebook);\r
593                                 } else {\r
594                                         logger.log(logger.EXTREME, "New dirty notebook found");\r
595                                         String oldGuid = enNotebook.getGuid();\r
596                                         boolean found = false;\r
597                                         \r
598                                         // Look for a notebook with the same name.  If one is found, we don't need \r
599                                         // to create another one\r
600                                         logger.log(logger.EXTREME, "Looking for matching notebook name");\r
601                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
602                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enNotebook.getName())) {\r
603                                                         enNotebook = remoteList.get(k);\r
604                                                         logger.log(logger.EXTREME, "Matching notebook found");\r
605                                                         found = true;\r
606                                                 }\r
607                                         }\r
608                                         if (!found)\r
609                                                 enNotebook = noteStore.createNotebook(authToken, enNotebook);\r
610                                         \r
611                                         logger.log(logger.EXTREME, "Updating notebook in database");\r
612                                         conn.getNotebookTable().updateNotebookGuid(oldGuid, enNotebook.getGuid());\r
613                                         sequence = enNotebook.getUpdateSequenceNum();\r
614                                 }\r
615                                 logger.log(logger.EXTREME, "Updating notebook sequence in database");\r
616                                 conn.getNotebookTable().updateNotebookSequence(enNotebook.getGuid(), sequence);\r
617                                 logger.log(logger.EXTREME, "Resetting dirty flag in notebook");\r
618                                 conn.getNotebookTable().resetDirtyFlag(enNotebook.getGuid());\r
619                                 updateSequenceNumber = sequence;\r
620                                 logger.log(logger.EXTREME, "Emitting sequence number to main thread");\r
621                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
622                         } catch (EDAMUserException e) {\r
623                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalNotebooks");\r
624                                 logger.log(logger.LOW, e.toString());   \r
625                                 error = true;\r
626                         } catch (EDAMSystemException e) {\r
627                                 logger.log(logger.LOW, "*** EDAM System Excepton syncLocalNotebooks");\r
628                                 logger.log(logger.LOW, e.toString());           \r
629                                 error = true;\r
630                         } catch (EDAMNotFoundException e) {\r
631                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalNotebooks");\r
632                                 logger.log(logger.LOW, e.toString());           \r
633                                 error = true;\r
634                         } catch (TException e) {\r
635                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalNotebooks");\r
636                                 logger.log(logger.LOW, e.toString());   \r
637                                 error = true;\r
638                         }               \r
639                 }\r
640                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalNotebooks");\r
641 \r
642         }\r
643         // Sync Tags with Evernote\r
644         private void syncLocalTags() {\r
645                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalTags");\r
646                 List<Tag> remoteList = new ArrayList<Tag>();\r
647                 status.message.emit("Sending local tags.");\r
648                 \r
649                 try {\r
650                         logger.log(logger.EXTREME, "Getting remote tags to compare names with the local tags");\r
651                         remoteList = noteStore.listTags(authToken);\r
652                 } catch (EDAMUserException e1) {\r
653                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote Tag List");\r
654                         status.message.emit("Error: " +e1);\r
655                         logger.log(logger.LOW, e1.toString());  \r
656                         error = true;\r
657                 } catch (EDAMSystemException e1) {\r
658                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote Tag List");\r
659                         status.message.emit("Error: " +e1);\r
660                         logger.log(logger.LOW, e1.toString());          \r
661                         error = true;\r
662                 } catch (TException e1) {\r
663                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote Tag List");\r
664                         status.message.emit("Error: " +e1);\r
665                         logger.log(logger.LOW, e1.toString());  \r
666                         error = true;\r
667                 }               \r
668                 \r
669                 int sequence;\r
670                 \r
671                 Tag enTag = findNextTag();\r
672                 while(enTag!=null) {\r
673                         if (authRefreshNeeded)\r
674                                 refreshConnection();\r
675 \r
676                         try {\r
677                                 if (enTag.getUpdateSequenceNum() > 0) {\r
678                                         logger.log(logger.EXTREME, "Updating tag");\r
679                                         sequence = noteStore.updateTag(authToken, enTag);\r
680                                 } else {\r
681                                         \r
682                                         // Look for a tag with the same name.  If one is found, we don't need \r
683                                         // to create another one\r
684                                         logger.log(logger.EXTREME, "New tag.  Comparing with remote names");\r
685                                         boolean found = false;\r
686                                         String oldGuid = enTag.getGuid();\r
687                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
688                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enTag.getName())) {\r
689                                                         conn.getTagTable().updateTagGuid(enTag.getGuid(), remoteList.get(k).getGuid());\r
690                                                         enTag = remoteList.get(k);\r
691                                                         logger.log(logger.EXTREME, "Matching tag name found");\r
692                                                         found = true;\r
693                                                 }\r
694                                         }\r
695                                         if (!found)\r
696                                                 enTag = noteStore.createTag(authToken, enTag);\r
697                                         else\r
698                                                 enTag.setUpdateSequenceNum(noteStore.updateTag(authToken,enTag));\r
699                                         sequence = enTag.getUpdateSequenceNum();\r
700                                         if (!oldGuid.equals(enTag.getGuid())) {\r
701                                                 logger.log(logger.EXTREME, "Updating tag guid");\r
702                                                 conn.getTagTable().updateTagGuid(oldGuid, enTag.getGuid());\r
703                                         }\r
704                                 }\r
705                                 logger.log(logger.EXTREME, "Updating tag sequence number");\r
706                                 conn.getTagTable().updateTagSequence(enTag.getGuid(), sequence);\r
707                                 logger.log(logger.EXTREME, "Resetting tag dirty flag");\r
708                                 conn.getTagTable().resetDirtyFlag(enTag.getGuid());\r
709                                 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");\r
710                                 updateSequenceNumber = sequence;\r
711                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
712                         } catch (EDAMUserException e) {\r
713                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");\r
714                                 logger.log(logger.LOW, e.toString());           \r
715                                 error = true;\r
716                         } catch (EDAMSystemException e) {\r
717                                 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");\r
718                                 logger.log(logger.LOW, e.toString());   \r
719                                 error = true;\r
720                         } catch (EDAMNotFoundException e) {\r
721                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");\r
722                                 logger.log(logger.LOW, e.toString());   \r
723                                 error = true;\r
724                         } catch (TException e) {\r
725                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");\r
726                                 logger.log(logger.LOW, e.toString());           \r
727                                 error = true;\r
728                         }       \r
729                         \r
730                         // Find the next tag\r
731                         logger.log(logger.EXTREME, "Finding next tag");\r
732                         enTag = findNextTag();\r
733                 }\r
734                 logger.log(logger.HIGH, "Leaving SyncRunner.syncLocalTags");\r
735         }\r
736         // Sync Tags with Evernote\r
737         private void syncLocalSavedSearches() {\r
738                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");\r
739                 List<SavedSearch> remoteList = new ArrayList<SavedSearch>();\r
740                 status.message.emit("Sending saved searches.");\r
741         \r
742                 logger.log(logger.EXTREME, "Getting saved searches to compare with local");\r
743                 try {\r
744                         remoteList = noteStore.listSearches(authToken);\r
745                 } catch (EDAMUserException e1) {\r
746                         logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags getting remote saved search List");\r
747                         status.message.emit("Error: " +e1);\r
748                         logger.log(logger.LOW, e1.toString());  \r
749                         error = true;\r
750                 } catch (EDAMSystemException e1) {\r
751                         logger.log(logger.LOW, "*** EDAM System Excepton syncLocalTags getting remote saved search List");\r
752                         status.message.emit("Error: " +e1);\r
753                         logger.log(logger.LOW, e1.toString());          \r
754                         error = true;\r
755                 } catch (TException e1) {\r
756                         logger.log(logger.LOW, "*** EDAM Transaction Excepton syncLocalTags getting remote saved search List");\r
757                         status.message.emit("Error: " +e1);\r
758                         logger.log(logger.LOW, e1.toString());  \r
759                         error = true;\r
760                 }               \r
761                 \r
762                 List<SavedSearch> searches = conn.getSavedSearchTable().getDirty();\r
763                 int sequence;\r
764                 // Sync the local notebooks with Evernote's\r
765                 logger.log(logger.EXTREME, "Beginning to send saved searches");\r
766                 for (int i=0; i<searches.size() &&  keepRunning; i++) {\r
767                         \r
768                         if (authRefreshNeeded)\r
769                                 refreshConnection();\r
770                         \r
771                         SavedSearch enSearch = searches.get(i);\r
772                         try {\r
773                                 if (enSearch.getUpdateSequenceNum() > 0) \r
774                                         sequence = noteStore.updateSearch(authToken, enSearch);\r
775                                 else {\r
776                                         logger.log(logger.EXTREME, "New saved search found.");\r
777                                         // Look for a tag with the same name.  If one is found, we don't need \r
778                                         // to create another one\r
779                                         boolean found = false;\r
780                                         logger.log(logger.EXTREME, "Matching remote saved search names with local");\r
781                                         for (int k=0; k<remoteList.size() && !found && keepRunning; k++) {\r
782                                                 if (remoteList.get(k).getName().equalsIgnoreCase(enSearch.getName())) {\r
783                                                         enSearch = remoteList.get(k);\r
784                                                         found = true;\r
785                                                         logger.log(logger.EXTREME, "Matching saved search found");\r
786                                                         sequence = enSearch.getUpdateSequenceNum();\r
787                                                 }\r
788                                         }\r
789 \r
790                                         String oldGuid = enSearch.getGuid();\r
791                                         if (!found)\r
792                                                 enSearch = noteStore.createSearch(authToken, enSearch);\r
793                                         sequence = enSearch.getUpdateSequenceNum();\r
794                                         logger.log(logger.EXTREME, "Updating tag guid in local database");\r
795                                         conn.getTagTable().updateTagGuid(oldGuid, enSearch.getGuid());\r
796                                 }\r
797                                 logger.log(logger.EXTREME, "Updating tag sequence in local database");\r
798                                 conn.getSavedSearchTable().updateSavedSearchSequence(enSearch.getGuid(), sequence);\r
799                                 logger.log(logger.EXTREME, "Resetting tag dirty flag");\r
800                                 conn.getSavedSearchTable().resetDirtyFlag(enSearch.getGuid());\r
801                                 logger.log(logger.EXTREME, "Emitting sequence number to the main thread.");\r
802                                 updateSequenceNumber = sequence;\r
803                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
804                         } catch (EDAMUserException e) {\r
805                                 logger.log(logger.LOW, "*** EDAM User Excepton syncLocalTags");\r
806                                 logger.log(logger.LOW, e.toString());   \r
807                                 error = true;\r
808                         } catch (EDAMSystemException e) {\r
809                                 logger.log(logger.LOW, "** EDAM System Excepton syncLocalTags");\r
810                                 logger.log(logger.LOW, e.toString());   \r
811                                 error = true;\r
812                         } catch (EDAMNotFoundException e) {\r
813                                 logger.log(logger.LOW, "*** EDAM Not Found Excepton syncLocalTags");\r
814                                 logger.log(logger.LOW, e.toString());   \r
815                                 error = true;\r
816                         } catch (TException e) {\r
817                                 logger.log(logger.LOW, "*** EDAM TExcepton syncLocalTags");\r
818                                 logger.log(logger.LOW, e.toString());   \r
819                                 error = true;\r
820                         }               \r
821                 }\r
822                 logger.log(logger.HIGH, "Entering SyncRunner.syncLocalSavedSearches");\r
823         }       \r
824 \r
825         // Sync evernote changes with local database\r
826         private void syncRemoteToLocal() {\r
827                 logger.log(logger.HIGH, "Entering SyncRunner.syncRemoteToLocal");\r
828 \r
829                 List<Note> dirtyNotes = conn.getNoteTable().getDirty();\r
830                 dirtyNoteGuids = new Vector<String>();\r
831                 for (int i=0; i<dirtyNotes.size() && keepRunning; i++) {\r
832                         dirtyNoteGuids.add(dirtyNotes.get(i).getGuid());\r
833                 }\r
834                 \r
835                 int chunkSize = 10;\r
836                 SyncChunk chunk = null;\r
837                 boolean fullSync = false;\r
838                 boolean more = true;\r
839                 \r
840                 if (updateSequenceNumber == 0)\r
841                         fullSync = true;\r
842                 \r
843                 status.message.emit("Downloading 0% complete.");\r
844                 \r
845                 while(more &&  keepRunning) {\r
846                         \r
847                         if (authRefreshNeeded)\r
848                                 refreshConnection();\r
849                         \r
850                         chunk = null;\r
851                         int sequence = updateSequenceNumber;\r
852                         try {\r
853                                 logger.log(logger.EXTREME, "Getting chunk from Evernote");\r
854                                 chunk = noteStore.getSyncChunk(authToken, sequence, chunkSize, fullSync);\r
855                         } catch (EDAMUserException e) {\r
856                                 error = true;\r
857                                 e.printStackTrace();\r
858                                 status.message.emit(e.getMessage());\r
859                         } catch (EDAMSystemException e) {\r
860                                 error = true;\r
861                                 e.printStackTrace();\r
862                                 status.message.emit(e.getMessage());\r
863                         } catch (TException e) {\r
864                                 error = true;\r
865                                 e.printStackTrace();\r
866                                 status.message.emit(e.getMessage());\r
867                         } \r
868                         if (error || chunk == null) \r
869                                 return;\r
870                                 \r
871                 \r
872                         \r
873                         syncRemoteTags(chunk.getTags());\r
874                         syncRemoteSavedSearches(chunk.getSearches());\r
875                         syncRemoteNotebooks(chunk.getNotebooks());\r
876                         syncRemoteNotes(chunk.getNotes(), fullSync);\r
877                         syncRemoteResources(chunk.getResources());\r
878                         \r
879                         // Do the local deletes\r
880                         logger.log(logger.EXTREME, "Doing local deletes");\r
881                         List<String> guid = chunk.getExpungedNotes();\r
882                         if (guid != null) \r
883                                 for (int i=0; i<guid.size() && keepRunning; i++) {\r
884                                         logger.log(logger.EXTREME, "Expunging local note from database");\r
885                                         conn.getNoteTable().expungeNote(guid.get(i), true, false);\r
886                                 }\r
887                         guid = chunk.getExpungedNotebooks();\r
888                         if (guid != null)\r
889                                 for (int i=0; i<guid.size() && keepRunning; i++) {\r
890                                         logger.log(logger.EXTREME, "Expunging local notebook from database");\r
891                                         conn.getNotebookTable().expungeNotebook(guid.get(i), false);\r
892                                 }\r
893                         guid = chunk.getExpungedTags();\r
894                         if (guid != null)\r
895                                 for (int i=0; i<guid.size() && keepRunning; i++) {\r
896                                         logger.log(logger.EXTREME, "Expunging tags from local database");\r
897                                         conn.getTagTable().expungeTag(guid.get(i), false);\r
898                                 }\r
899                         guid = chunk.getExpungedSearches();\r
900                         if (guid != null) \r
901                                 for (int i=0; i<guid.size() && keepRunning; i++) {\r
902                                         logger.log(logger.EXTREME, "Expunging saved search from local database");\r
903                                         conn.getSavedSearchTable().expungeSavedSearch(guid.get(i), false);\r
904                                 }\r
905 \r
906                         \r
907                         // Check for more notes\r
908                         if (chunk.getChunkHighUSN() <= updateSequenceNumber) \r
909                                 more = false;\r
910                         if (error)\r
911                                 more = false;\r
912                         logger.log(logger.EXTREME, "More notes? " +more);\r
913 \r
914                         \r
915                         // Save the chunk sequence number\r
916                         if (!error && chunk.getChunkHighUSN() > 0) {\r
917                                 logger.log(logger.EXTREME, "emitting sequence number to main thread");\r
918                                 updateSequenceNumber = chunk.getChunkHighUSN();\r
919                                 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());\r
920                                 conn.getSyncTable().setUpdateSequenceNumber(updateSequenceNumber);\r
921                         }\r
922                         \r
923                         \r
924                         if (more) {\r
925                                 long pct = chunk.getChunkHighUSN() * 100;\r
926                                 conn.getSyncTable().setLastSequenceDate(chunk.getCurrentTime());\r
927                                 pct = pct/evernoteUpdateCount;\r
928                                 status.message.emit("Downloading " +new Long(pct).toString()+"% complete.");\r
929                         }\r
930                 }\r
931 \r
932                 logger.log(logger.HIGH, "Leaving SyncRunner.syncRemoteToLocal");\r
933         }\r
934         // Sync remote tags\r
935         private void syncRemoteTags(List<Tag> tags) {\r
936                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteTags");\r
937                 if (tags != null) {\r
938                         for (int i=0; i<tags.size() && keepRunning; i++) {\r
939                                 String oldGuid;\r
940                                 oldGuid = conn.getTagTable().findTagByName(tags.get(i).getName());\r
941                                 if (oldGuid != null && !tags.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
942                                         conn.getTagTable().updateTagGuid(oldGuid, tags.get(i).getGuid());\r
943                                 conn.getTagTable().syncTag(tags.get(i), false);\r
944                         }\r
945                 }\r
946                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteTags");\r
947         }\r
948         // Sync remote tags\r
949         private void syncRemoteSavedSearches(List<SavedSearch> searches) {\r
950                 logger.log(logger.EXTREME, "Entering SyncRunner.syncSavedSearches");\r
951                 if (searches != null) {\r
952                         for (int i=0; i<searches.size() && keepRunning; i++) {\r
953                                 String oldGuid;\r
954                                 oldGuid = conn.getSavedSearchTable().findSavedSearchByName(searches.get(i).getName());\r
955                                 if (oldGuid != null && !searches.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
956                                         conn.getSavedSearchTable().updateSavedSearchGuid(oldGuid, searches.get(i).getGuid());\r
957                                 conn.getSavedSearchTable().syncSavedSearch(searches.get(i), false);\r
958                         }\r
959                 }\r
960                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncSavedSearches");\r
961         }\r
962         // Sync remote Notebooks 2\r
963         private void syncRemoteNotebooks(List<Notebook> notebooks) {\r
964                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotebooks");\r
965                 if (notebooks != null) {\r
966                         for (int i=0; i<notebooks.size() && keepRunning; i++) {\r
967                                 String oldGuid;\r
968                                 oldGuid = conn.getNotebookTable().findNotebookByName(notebooks.get(i).getName());\r
969                                 if (oldGuid != null && !conn.getNotebookTable().isNotebookLocal(oldGuid) && !notebooks.get(i).getGuid().equalsIgnoreCase(oldGuid))\r
970                                         conn.getNotebookTable().updateNotebookGuid(oldGuid, notebooks.get(i).getGuid());\r
971                                 conn.getNotebookTable().syncNotebook(notebooks.get(i), false); \r
972                         }\r
973                 }                       \r
974                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotebooks");\r
975         }\r
976         // Sync remote Resources\r
977         private void syncRemoteResources(List<Resource> resource) {\r
978                 // This is how the logic for this works.\r
979                 // 1.) If the resource is not in the local database, we add it.\r
980                 // 2.) If a copy of the resource is in the local database and the note isn't dirty, we update the local copy\r
981                 // 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
982                 //     is a conflict.  The note conflict should get a copy of the resource at that time.\r
983                 \r
984                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteResources");\r
985                 if (resource != null) {\r
986                         for (int i=0; i<resource.size() && keepRunning; i++) {\r
987                                 boolean saveNeeded = false;\r
988 /* #1 */                Resource r = getEvernoteResource(resource.get(i).getGuid(), true,true,true);\r
989                                 Resource l = conn.getNoteTable().noteResourceTable.getNoteResource(r.getGuid(), false);\r
990                                 if (l == null) {\r
991                                         saveNeeded = true;\r
992                                 } else {\r
993 /* #2 */                        boolean isNoteDirty = conn.getNoteTable().isNoteDirty(r.getNoteGuid());\r
994                                         if (!isNoteDirty)\r
995                                                 saveNeeded = true;\r
996                                         else {\r
997 /* #3 */                                String remoteHash = "";\r
998                                                 if (r != null && r.getData() != null && r.getData().getBodyHash() != null)\r
999                                                         remoteHash = byteArrayToHexString(r.getData().getBodyHash());\r
1000                                                 String localHash = "";\r
1001                                                 if (l != null && l.getData() != null && l.getData().getBodyHash() != null)\r
1002                                                         remoteHash = byteArrayToHexString(l.getData().getBodyHash());\r
1003                                 \r
1004                                                 if (localHash.equalsIgnoreCase(remoteHash))\r
1005                                                         saveNeeded = true;\r
1006                                         }\r
1007                                 }\r
1008                                 \r
1009                                 if (saveNeeded) \r
1010                                         conn.getNoteTable().noteResourceTable.updateNoteResource(r, false);\r
1011 \r
1012                         }\r
1013                 }\r
1014                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteResources");\r
1015         }\r
1016         // Sync remote notebooks\r
1017         private void syncRemoteNotes(List<Note> note, boolean fullSync) {\r
1018                 logger.log(logger.EXTREME, "Entering SyncRunner.syncRemoteNotes");\r
1019                 if (note != null) {\r
1020                         for (int i=0; i<note.size() && keepRunning; i++) {\r
1021                                 Note n = getEvernoteNote(note.get(i).getGuid(), true, fullSync, true,true);\r
1022                                 if (n!=null) {\r
1023                                         \r
1024                                         // Basically, this is how the sync logic for a note works.\r
1025                                         // If the remote note has changed and the local has not, we\r
1026                                         // accept the change.\r
1027                                         // If both the local & remote have changed but the sequence\r
1028                                         // numbers are the same, we don't accept the change.  This\r
1029                                         // seems to happen when attachments are indexed by the server.\r
1030                                         // If both the local & remote have changed and the sequence numbers\r
1031                                         // are different we move the local copy to a local notebook (making sure\r
1032                                         // to copy all resources) and we accept the new one.                    \r
1033                                         boolean conflictingNote = true;\r
1034                                         logger.log(logger.EXTREME, "Checking for duplicate note " +n.getGuid());\r
1035                                         if (dirtyNoteGuids.contains(n.getGuid())) { \r
1036                                                 logger.log(logger.EXTREME, "Conflict check beginning");\r
1037                                                 conflictingNote = checkForConflict(n);\r
1038                                                 logger.log(logger.EXTREME, "Conflict check results " +conflictingNote);\r
1039                                                 if (conflictingNote)\r
1040                                                         moveConflictingNote(n.getGuid());\r
1041                                         }\r
1042                                         if (conflictingNote || fullSync) {\r
1043                                                 logger.log(logger.EXTREME, "Saving Note");\r
1044                                                 conn.getNoteTable().syncNote(n, false);\r
1045                                                 noteSignal.noteChanged.emit(n.getGuid(), null);   // Signal to ivalidate note cache\r
1046                                                 logger.log(logger.EXTREME, "Note Saved");\r
1047                                                 if (fullSync && n.getResources() != null) {\r
1048                                                         for (int q=0; q<n.getResources().size() && keepRunning; q++) {\r
1049                                                                 logger.log(logger.EXTREME, "Getting note resources.");\r
1050                                                                 conn.getNoteTable().noteResourceTable.updateNoteResource(n.getResources().get(q), false);\r
1051                                                         }\r
1052                                                 }\r
1053                                         }\r
1054                                 }\r
1055                         }\r
1056                 }\r
1057                 logger.log(logger.EXTREME, "Leaving SyncRunner.syncRemoteNotes");\r
1058         }\r
1059         private Note getEvernoteNote(String guid, boolean withContent, boolean withResourceData, boolean withResourceRecognition, boolean withResourceAlternateData) { \r
1060                 Note n = null;\r
1061                 try {\r
1062                         logger.log(logger.EXTREME, "Retrieving note " +guid);\r
1063                         n = noteStore.getNote(authToken, guid, withContent, withResourceData, withResourceRecognition, withResourceAlternateData);\r
1064                         logger.log(logger.EXTREME, "Note " +guid +" has been retrieved.");\r
1065                 } catch (EDAMUserException e) {\r
1066                         logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
1067                         logger.log(logger.LOW, e.toString());   \r
1068                         error = true;\r
1069                         e.printStackTrace();\r
1070                 } catch (EDAMSystemException e) {\r
1071                         logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");\r
1072                         logger.log(logger.LOW, e.toString());   \r
1073                         error = true;\r
1074                         e.printStackTrace();\r
1075                 } catch (EDAMNotFoundException e) {\r
1076                         logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");\r
1077                         logger.log(logger.LOW, e.toString());   \r
1078                         error = true;\r
1079                         e.printStackTrace();\r
1080                 } catch (TException e) {\r
1081                         logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");\r
1082                         logger.log(logger.LOW, e.toString());   \r
1083                         error = true;\r
1084                         e.printStackTrace();\r
1085                 }\r
1086                 return n;\r
1087         }\r
1088         private Resource getEvernoteResource(String guid, boolean withData, boolean withRecognition, boolean withAttributes) { \r
1089                 Resource n = null;\r
1090                 try {\r
1091                         logger.log(logger.EXTREME, "Retrieving resource " +guid);\r
1092                         n = noteStore.getResource(authToken, guid, withData, withRecognition, withAttributes, withAttributes);\r
1093                         logger.log(logger.EXTREME, "Resource " +guid +" has been retrieved.");\r
1094                 } catch (EDAMUserException e) {\r
1095                         logger.log(logger.LOW, "*** EDAM User Excepton getEvernoteNote");\r
1096                         logger.log(logger.LOW, e.toString());   \r
1097                         error = true;\r
1098                         e.printStackTrace();\r
1099                 } catch (EDAMSystemException e) {\r
1100                         logger.log(logger.LOW, "*** EDAM System Excepton getEvernoteNote");\r
1101                         logger.log(logger.LOW, e.toString());   \r
1102                         error = true;\r
1103                         e.printStackTrace();\r
1104                 } catch (EDAMNotFoundException e) {\r
1105                         logger.log(logger.LOW, "*** EDAM Not Found Excepton getEvernoteNote");\r
1106                         logger.log(logger.LOW, e.toString());   \r
1107                         error = true;\r
1108                         e.printStackTrace();\r
1109                 } catch (TException e) {\r
1110                         logger.log(logger.LOW, "*** EDAM TExcepton getEvernoteNote");\r
1111                         logger.log(logger.LOW, e.toString());   \r
1112                         error = true;\r
1113                         e.printStackTrace();\r
1114                 }\r
1115                 return n;\r
1116         }\r
1117 \r
1118         \r
1119         private boolean checkForConflict(Note n) {\r
1120                 logger.log(logger.EXTREME, "Checking note sequence number  " +n.getGuid());\r
1121                 Note oldNote = conn.getNoteTable().getNote(n.getGuid(), false, false, false, false, false);\r
1122                 logger.log(logger.EXTREME, "Local/Remote sequence numbers: " +oldNote.getUpdateSequenceNum()+"/"+n.getUpdateSequenceNum());\r
1123                 if (oldNote.getUpdateSequenceNum() == n.getUpdateSequenceNum())\r
1124                         return false;\r
1125                 return true;\r
1126         }\r
1127         \r
1128         private void moveConflictingNote(String guid) {\r
1129                 logger.log(logger.EXTREME, "Conflicting change found for note " +guid);\r
1130                 List<Notebook> books = conn.getNotebookTable().getAllLocal();\r
1131                 String notebookGuid = null;\r
1132                 for (int i=0; i<books.size() && keepRunning; i++) {\r
1133                         if (books.get(i).getName().equalsIgnoreCase("Conflicting Changes (local)") ||\r
1134                                         books.get(i).getName().equalsIgnoreCase("Conflicting Changes")) {\r
1135                                 notebookGuid = books.get(i).getGuid();\r
1136                                 i=books.size();\r
1137                         }\r
1138                 }\r
1139                 \r
1140                 if (notebookGuid == null) {\r
1141                         logger.log(logger.EXTREME, "Building conflicting change notebook " +guid);\r
1142                         Calendar currentTime = new GregorianCalendar();\r
1143                         Long l = new Long(currentTime.getTimeInMillis());\r
1144                         long prevTime = l;\r
1145                         while (prevTime==l) {\r
1146                                 currentTime = new GregorianCalendar();\r
1147                                 l=currentTime.getTimeInMillis();\r
1148                         }\r
1149                         String randint = new String(Long.toString(l));\r
1150                 \r
1151                         Notebook newBook = new Notebook();\r
1152                         newBook.setUpdateSequenceNum(0);\r
1153                         newBook.setGuid(randint);\r
1154                         newBook.setName("Conflicting Changes");\r
1155                         newBook.setServiceCreated(new Date().getTime());\r
1156                         newBook.setServiceUpdated(new Date().getTime());\r
1157                         newBook.setDefaultNotebook(false);\r
1158                         newBook.setPublished(false);\r
1159                         \r
1160                         conn.getNotebookTable().addNotebook(newBook, false, true);\r
1161                         notebookGuid = newBook.getGuid();\r
1162                 }\r
1163                 \r
1164                 // Now that we have a good notebook guid, we need to move the conflicting note\r
1165                 // to the local notebook\r
1166                 logger.log(logger.EXTREME, "Moving conflicting note " +guid);\r
1167                 Calendar currentTime = new GregorianCalendar();\r
1168                 Long l = new Long(currentTime.getTimeInMillis());\r
1169                 long prevTime = l;\r
1170                 while (prevTime==l) {\r
1171                         currentTime = new GregorianCalendar();\r
1172                         l = currentTime.getTimeInMillis();\r
1173                 }\r
1174                 String newGuid = new String(Long.toString(l));\r
1175                                         \r
1176                 Note oldNote = conn.getNoteTable().getNote(guid, true, true, false, false, false);\r
1177                 for (int i=0; i<oldNote.getResources().size() && keepRunning; i++) {\r
1178                         l = new Long(currentTime.getTimeInMillis());\r
1179                         String newResG = new String(Long.toString(l));\r
1180                         String oldResG = oldNote.getResources().get(i).getGuid();\r
1181                         conn.getNoteTable().noteResourceTable.resetUpdateSequenceNumber(oldResG, true);\r
1182                         conn.getNoteTable().noteResourceTable.updateNoteResourceGuid(oldResG, newResG, true);\r
1183                 }\r
1184                 \r
1185                 conn.getNoteTable().resetNoteSequence(guid);\r
1186                 conn.getNoteTable().updateNoteGuid(guid, newGuid);\r
1187                 conn.getNoteTable().updateNoteNotebook(newGuid, notebookGuid, true);\r
1188                 \r
1189                 \r
1190                 noteSignal.guidChanged.emit(guid,newGuid);\r
1191         }\r
1192         \r
1193         \r
1194 \r
1195         \r
1196         //******************************************************\r
1197         //******************************************************\r
1198         //** Utility Functions\r
1199         //******************************************************\r
1200         //******************************************************\r
1201         // Convert a byte array to a hex string\r
1202         private static String byteArrayToHexString(byte data[]) {\r
1203                 StringBuffer buf = new StringBuffer();\r
1204             for (byte element : data) {\r
1205                 int halfbyte = (element >>> 4) & 0x0F;\r
1206                 int two_halfs = 0;\r
1207                 do {\r
1208                         if ((0 <= halfbyte) && (halfbyte <= 9))\r
1209                                buf.append((char) ('0' + halfbyte));\r
1210                            else\r
1211                                 buf.append((char) ('a' + (halfbyte - 10)));\r
1212                         halfbyte = element & 0x0F;\r
1213                 } while(two_halfs++ < 1);\r
1214             }\r
1215             return buf.toString();              \r
1216         }\r
1217 \r
1218         \r
1219         \r
1220         //*******************************************************\r
1221         //* Find dirty tags, which do not have newly created parents\r
1222         //*******************************************************\r
1223         private Tag findNextTag() {\r
1224                 logger.log(logger.HIGH, "Entering SyncRunner.findNextTag");\r
1225                 Tag nextTag = null;\r
1226                 List<Tag> tags = conn.getTagTable().getDirty();\r
1227                 \r
1228                 // Find the parent.  If the parent has a sequence > 0 then it is a good\r
1229                 // parent.\r
1230                 for (int i=0; i<tags.size() && keepRunning; i++) {\r
1231                         if (tags.get(i).getParentGuid() == null) {\r
1232                                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found without parent");\r
1233                                 return tags.get(i);\r
1234                         }\r
1235                         Tag parentTag = conn.getTagTable().getTag(tags.get(i).getParentGuid());\r
1236                         if (parentTag.getUpdateSequenceNum() > 0) {\r
1237                                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - tag found");\r
1238                                 return tags.get(i);\r
1239                         }\r
1240                 }\r
1241                 \r
1242                 logger.log(logger.HIGH, "Leaving SyncRunner.findNextTag - no tags returned");\r
1243                 return nextTag;\r
1244         }\r
1245         \r
1246         \r
1247            // Connect to Evernote\r
1248     public boolean enConnect()  {\r
1249         try {\r
1250                         userStoreTrans = new THttpClient(userStoreUrl);\r
1251                 } catch (TTransportException e) {\r
1252                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1253                         mb.exec();\r
1254                         e.printStackTrace();\r
1255                 }\r
1256                 userStoreProt = new TBinaryProtocol(userStoreTrans);\r
1257             userStore = new UserStore.Client(userStoreProt, userStoreProt);\r
1258             syncSignal.saveUserStore.emit(userStore);\r
1259             try {\r
1260                         authResult = userStore.authenticate(username, password, consumerKey, consumerSecret);\r
1261                 } catch (EDAMUserException e) {\r
1262                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Error", "Incorrect username/password");\r
1263                         mb.exec();\r
1264                         isConnected = false;\r
1265                         return false;\r
1266                 } catch (EDAMSystemException e) {\r
1267                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "EDAM System Excepton", e.getLocalizedMessage());\r
1268                         mb.exec();\r
1269                         e.printStackTrace();\r
1270                         isConnected = false;\r
1271                 } catch (TException e) {\r
1272                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1273                         mb.exec();\r
1274                         e.printStackTrace();\r
1275                         isConnected = false;\r
1276                 }\r
1277                 \r
1278             boolean versionOk = false;\r
1279                 try {\r
1280 //                      versionOk = userStore.checkVersion("Dave's EDAMDemo (Java)", \r
1281                         versionOk = userStore.checkVersion("NeverNote", \r
1282                     com.evernote.edam.userstore.Constants.EDAM_VERSION_MAJOR, \r
1283                       com.evernote.edam.userstore.Constants.EDAM_VERSION_MINOR);\r
1284                 } catch (TException e) {\r
1285                         e.printStackTrace();\r
1286                         isConnected = false;\r
1287                 } \r
1288             if (!versionOk) { \r
1289                 System.err.println("Incomatible EDAM client protocol version"); \r
1290                 isConnected = false;\r
1291             }\r
1292             if (authResult != null) {\r
1293                 user = authResult.getUser(); \r
1294                 authToken = authResult.getAuthenticationToken(); \r
1295                 noteStoreUrl = noteStoreUrlBase + user.getShardId();\r
1296                 syncSignal.saveAuthToken.emit(authToken);\r
1297                 syncSignal.saveNoteStore.emit(noteStore);\r
1298                 \r
1299          \r
1300                 try {\r
1301                         noteStoreTrans = new THttpClient(noteStoreUrl);\r
1302                 } catch (TTransportException e) {\r
1303                         QMessageBox mb = new QMessageBox(QMessageBox.Icon.Critical, "Transport Excepton", e.getLocalizedMessage());\r
1304                         mb.exec();\r
1305                         e.printStackTrace();\r
1306                         isConnected = false;\r
1307                 } \r
1308                 noteStoreProt = new TBinaryProtocol(noteStoreTrans);\r
1309                 noteStore = \r
1310                         new NoteStore.Client(noteStoreProt, noteStoreProt); \r
1311                 isConnected = true;\r
1312                 authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();\r
1313                 authRefreshTime = authTimeRemaining / 2;\r
1314             }\r
1315             \r
1316                 // Get user information\r
1317                 try {\r
1318                         User user = userStore.getUser(authToken);\r
1319                         syncSignal.saveUserInformation.emit(user);\r
1320                 } catch (EDAMUserException e1) {\r
1321                         e1.printStackTrace();\r
1322                 } catch (EDAMSystemException e1) {\r
1323                         e1.printStackTrace();\r
1324                 } catch (TException e1) {\r
1325                         e1.printStackTrace();\r
1326                 }\r
1327             \r
1328             return isConnected;\r
1329     }\r
1330         // Disconnect from the database                         \r
1331     public void enDisconnect() {\r
1332         isConnected = false;\r
1333     }\r
1334     // Refresh the connection\r
1335     private synchronized void refreshConnection() {\r
1336                 logger.log(logger.EXTREME, "Entering SyncRunner.refreshConnection()");\r
1337 //        Calendar cal = Calendar.getInstance();\r
1338                 \r
1339         // If we are not connected let's get out of here\r
1340         if (!isConnected)\r
1341                 return;\r
1342         \r
1343                 // If we fail too many times, then let's give up.\r
1344                 if (failedRefreshes >=5) {\r
1345                         logger.log(logger.EXTREME, "Refresh attempts have failed.  Disconnecting.");\r
1346                         isConnected = false;\r
1347                         return;\r
1348                 }\r
1349         \r
1350                 // If this is the first time through, then we need to set this\r
1351 //              if (authRefreshTime == 0 || cal.getTimeInMillis() > authRefreshTime) \r
1352 //                      authRefreshTime = cal.getTimeInMillis();\r
1353                 \r
1354  //             // Default to checking again in 5 min.  This in case we fail.\r
1355  //             authRefreshTime = authRefreshTime +(5*60*1000);     \r
1356 \r
1357                 // Try to get a new token\r
1358                 AuthenticationResult newAuth = null; \r
1359                 logger.log(logger.EXTREME, "Beginning to try authentication refresh");\r
1360         try {\r
1361                 if (userStore != null && authToken != null) \r
1362                         newAuth = userStore.refreshAuthentication(authToken); \r
1363                 else\r
1364                         return;\r
1365                 logger.log(logger.EXTREME, "UserStore.refreshAuthentication has succeeded.");\r
1366                 } catch (EDAMUserException e) {\r
1367                         e.printStackTrace();\r
1368                         syncSignal.authRefreshComplete.emit(false);\r
1369                         failedRefreshes++;\r
1370                         return;\r
1371                 } catch (EDAMSystemException e) {\r
1372                         e.printStackTrace();\r
1373                         syncSignal.authRefreshComplete.emit(false);\r
1374                         failedRefreshes++;\r
1375                         return;         \r
1376                 } catch (TException e) { \r
1377                         e.printStackTrace();\r
1378                         syncSignal.authRefreshComplete.emit(false);\r
1379                         failedRefreshes++;\r
1380                         return;\r
1381                 }\r
1382                 \r
1383                 // If we didn't get a good auth, then we've failed\r
1384                 if (newAuth == null) {\r
1385                         failedRefreshes++;\r
1386                         logger.log(logger.EXTREME, "Authentication failure #" +failedRefreshes);\r
1387                         syncSignal.authRefreshComplete.emit(false);\r
1388                         return;\r
1389                 }\r
1390                 \r
1391                 // We got a good token.  Now we need to setup the time to renew it.\r
1392                 logger.log(logger.EXTREME, "Saving authentication tokens");\r
1393                 authResult = newAuth;\r
1394                 authToken = new String(newAuth.getAuthenticationToken());\r
1395 //              authTimeRemaining = authResult.getExpiration() - authResult.getCurrentTime();\r
1396 //              authRefreshTime = cal.getTimeInMillis() + (authTimeRemaining/4);        \r
1397                 failedRefreshes=0;\r
1398                 syncSignal.authRefreshComplete.emit(true);\r
1399                 authRefreshNeeded = false;\r
1400                         \r
1401                 // This should never happen, but if it does we consider this a faild attempt.\r
1402 //              if (authTimeRemaining <= 0) {\r
1403 //                      failedRefreshes++;\r
1404 //                      syncSignal.authRefreshComplete.emit(false);\r
1405 //              }\r
1406     }\r
1407         \r
1408         public synchronized boolean addWork(String request) {\r
1409                 if (workQueue.offer(request))\r
1410                         return true;\r
1411                 return false;\r
1412         }\r
1413     \r
1414     private Note getNoteContent(Note n) {\r
1415         n.setContent(conn.getNoteTable().getNoteContentBinary(n.getGuid()));\r
1416         return n;\r
1417     }\r
1418 }\r