@SuppressWarnings("unchecked") private void garbageCollectIdentities() { final MessageManager messageManager = mFreetalk.getMessageManager(); final PersistentTaskManager taskManager = mFreetalk.getTaskManager(); synchronized (this) { if (mIdentityFetchInProgress || mOwnIdentityFetchInProgress || mLastIdentityFetchTime == 0 || mLastOwnIdentityFetchTime == 0) return; /* Executing the thread loop once will always take longer than THREAD_PERIOD. Therefore, if we set the limit to 3*THREAD_PERIOD, * it will hit identities which were last received before more than 2*THREAD_LOOP, not exactly 3*THREAD_LOOP. */ long lastAcceptTime = Math.min(mLastIdentityFetchTime, mLastOwnIdentityFetchTime) - GARBAGE_COLLECT_DELAY; lastAcceptTime = Math.max( lastAcceptTime, 0); // This is not really needed but a time less than 0 does not make sense.; Query q = db.query(); q.constrain(WoTIdentity.class); q.descend("mLastReceivedFromWoT").constrain(lastAcceptTime).smaller(); ObjectSet<WoTIdentity> result = q.execute(); for (WoTIdentity identity : result) { identity.initializeTransient(mFreetalk); Logger.debug(this, "Garbage collecting identity " + identity); deleteIdentity(identity, messageManager, taskManager); } if (mShortestUniqueNicknameCacheNeedsUpdate) updateShortestUniqueNicknameCache(); } }
/** * Checks for duplicate identity objects and deletes duplicates if they exist. I have absolutely * NO idea why Bombe does happen to have a duplicate identity, I see no code path which could * cause this. TODO: Get rid of this function if nobody reports a duplicate for some time - the * function was added at 2011-01-10 */ private synchronized void deleteDuplicateIdentities() { WoTMessageManager messageManager = mFreetalk.getMessageManager(); PersistentTaskManager taskManager = mFreetalk.getTaskManager(); synchronized (messageManager) { synchronized (taskManager) { synchronized (db.lock()) { try { HashSet<String> deleted = new HashSet<String>(); Logger.normal(this, "Searching for duplicate identities ..."); for (WoTIdentity identity : getAllIdentities()) { Query q = db.query(); q.constrain(WoTIdentity.class); q.descend("mID").constrain(identity.getID()); q.constrain(identity).identity().not(); ObjectSet<WoTIdentity> duplicates = new Persistent.InitializingObjectSet<WoTIdentity>(mFreetalk, q); for (WoTIdentity duplicate : duplicates) { if (deleted.contains(duplicate.getID()) == false) { Logger.error( duplicate, "Deleting duplicate identity " + duplicate.getRequestURI()); deleteIdentity(duplicate, messageManager, taskManager); } } deleted.add(identity.getID()); } Persistent.checkedCommit(db, this); Logger.normal(this, "Finished searching for duplicate identities."); } catch (RuntimeException e) { Persistent.checkedRollback(db, this, e); } } } } }
@SuppressWarnings("unchecked") private void parseIdentities(SimpleFieldSet params, boolean bOwnIdentities) { if (bOwnIdentities) Logger.normal(this, "Parsing received own identities..."); else Logger.normal(this, "Parsing received identities..."); int idx; int ignoredCount = 0; int newCount = 0; for (idx = 0; ; idx++) { String identityID = params.get("Identity" + idx); if (identityID == null || identityID.equals("")) /* TODO: Figure out whether the second condition is necessary */ break; String requestURI = params.get("RequestURI" + idx); String insertURI = bOwnIdentities ? params.get("InsertURI" + idx) : null; String nickname = params.get("Nickname" + idx); if (nickname == null || nickname.length() == 0) { // If an identity publishes an invalid nickname in one of its first WoT inserts then WoT // will return an empty // nickname for that identity until a new XML was published with a valid nickname. We ignore // the identity until // then to prevent confusing error logs. // TODO: Maybe handle this in WoT. Would require checks in many places though. continue; } synchronized (this) { /* We lock here and not during the whole function to allow other threads to execute */ Query q = db.query(); // TODO: Encapsulate the query in a function... q.constrain(WoTIdentity.class); q.descend("mID").constrain(identityID); ObjectSet<WoTIdentity> result = q.execute(); WoTIdentity id = null; if (result.size() == 0) { try { importIdentity(bOwnIdentities, identityID, requestURI, insertURI, nickname); ++newCount; } catch (Exception e) { Logger.error(this, "Importing a new identity failed.", e); } } else { Logger.debug(this, "Not importing already existing identity " + requestURI); ++ignoredCount; assert (result.size() == 1); id = result.next(); id.initializeTransient(mFreetalk); if (bOwnIdentities != (id instanceof WoTOwnIdentity)) { // The type of the identity changed so we need to delete and re-import it. try { Logger.normal(this, "Identity type changed, replacing it: " + id); // We MUST NOT take the following locks because deleteIdentity does other locks // (MessageManager/TaskManager) which must happen before... // synchronized(id) // synchronized(db.lock()) deleteIdentity(id, mFreetalk.getMessageManager(), mFreetalk.getTaskManager()); importIdentity(bOwnIdentities, identityID, requestURI, insertURI, nickname); } catch (Exception e) { Logger.error(this, "Replacing a WoTIdentity with WoTOwnIdentity failed.", e); } } else { // Normal case: Update the last received time of the idefnt synchronized (id) { synchronized (db.lock()) { try { // TODO: The thread sometimes takes hours to parse the identities and I don't know // why. // So right now its better to re-query the time for each identity. id.setLastReceivedFromWoT(CurrentTimeUTC.getInMillis()); id.checkedCommit(this); } catch (Exception e) { Persistent.checkedRollback(db, this, e); } } } } } } Thread.yield(); } Logger.normal( this, "parseIdentities(bOwnIdentities==" + bOwnIdentities + " received " + idx + " identities. Ignored " + ignoredCount + "; New: " + newCount); }