public synchronized void process() {
    WoTIdentityManager identityManager = (WoTIdentityManager) mFreetalk.getIdentityManager();

    long now = CurrentTimeUTC.getInMillis();

    try {
      MessageManager messageManager = mFreetalk.getMessageManager();

      // We must tell the user to solve puzzles if he as written a message ...
      if (messageManager.getOwnMessagesBy(mOwner).size() > 0
          || messageManager.getMessagesBy(mOwner).size()
              > 0) { // Also check for messages which are not stored as own messages anymore.

        int minimumTrusterCount = mFreetalk.getConfig().getInt(Config.MINIMUM_TRUSTER_COUNT);

        // ... and if he has not received enough trust values.
        if (identityManager.getReceivedTrustsCount(mOwner) < minimumTrusterCount) {
          mPuzzlesToSolve = minimumTrusterCount * 2;
          mNextDisplayTime = now;
          mNextProcessingTime =
              Long.MAX_VALUE; // Task is in display mode now, no need to proccess it anymore
          storeAndCommit();
          return;
        }
      }

      mNextProcessingTime = now + PROCESSING_INTERVAL;
      storeAndCommit();
      return;

    } catch (Exception e) {
      mNextProcessingTime = now + PROCESSING_INTERVAL_SHORT;
      Logger.error(this, "Error while processing an IntroduceIdentityTask", e);
    }
  }
  public synchronized void onHideForSomeTime() {
    mPuzzlesToSolve = 0;
    mNextProcessingTime = CurrentTimeUTC.getInMillis() + PROCESSING_INTERVAL;
    mNextDisplayTime = Long.MAX_VALUE;

    storeAndCommit();
  }
  /**
   * Retrieves the session ID from the session cookie in the given {@link ToadletContext}, checks if
   * it contains a valid (existing and not expired) session and if yes, returns the {@link Session}.
   *
   * <p>If the session was valid, then its validity is extended by {@link MAX_SESSION_IDLE_TIME}.
   *
   * <p>If the session is not valid anymore, <code>null</code> is returned if the new constructor
   * was used (for example by PluginRespirator). If the deprecated constructor was used, a
   * RedirectException to the login URI is thrown.
   *
   * @throws RedirectException if login redirect URI was set
   */
  public synchronized Session useSession(ToadletContext context) throws RedirectException {
    UUID sessionID = getSessionID(context);
    if (sessionID == null) {
      if (mLogInRedirectURI == null) return null;
      throw new RedirectException(mLogInRedirectURI);
    }

    // We must synchronize around the fetching of the time and mSessionsByID.push() because
    // mSessionsByID is no sorting data structure: It's a plain
    // LRUHashtable so to ensure that it stays sorted the operation "getTime(); push();" must be
    // atomic.
    long time = CurrentTimeUTC.getInMillis();

    removeExpiredSessions(time);

    Session session = mSessionsByID.get(sessionID);

    if (session == null) {
      if (mLogInRedirectURI == null) return null;
      throw new RedirectException(mLogInRedirectURI);
    }

    session.updateExpiresAtTime(time);
    mSessionsByID.push(session.getID(), session);

    setSessionCookie(session, context);

    return session;
  }
 protected PersistentTask(OwnIdentity myOwner) {
   mID = UUID.randomUUID().toString();
   mOwner = myOwner;
   mNextProcessingTime = CurrentTimeUTC.getInMillis();
   mNextDisplayTime = Long.MAX_VALUE;
   mDeleteTime = Long.MAX_VALUE;
 }
  /**
   * Returns true if the given {@link ToadletContext} contains a session cookie for a valid
   * (existing and not expired) session.
   *
   * <p>In opposite to {@link getSessionUserID}, this function does NOT extend the validity of the
   * session. Therefore, this function can be considered as a way of peeking for a session, to
   * decide which Toadlet links should be visible.
   */
  public synchronized boolean sessionExists(ToadletContext context) {
    UUID sessionID = getSessionID(context);

    if (sessionID == null) return false;

    removeExpiredSessions(CurrentTimeUTC.getInMillis());

    return mSessionsByID.containsKey(sessionID);
  }
  public synchronized void onPuzzleSolved() {
    if (mPuzzlesToSolve > 0) --mPuzzlesToSolve;

    if (mPuzzlesToSolve == 0) {
      mNextProcessingTime = CurrentTimeUTC.getInMillis() + PROCESSING_INTERVAL;
      mNextDisplayTime = Long.MAX_VALUE;
    }

    storeAndCommit();
  }
  /**
   * Fetches the identities with positive score from WoT and stores them in the database.
   *
   * @throws Exception
   */
  private void fetchIdentities() throws Exception {
    // parseIdentities() acquires and frees the WoTIdentityManager-lock for each identity to allow
    // other threads to access the identity manager while the
    // parsing is in progress. Therefore, we do not take the lock for the whole execution of this
    // function.
    synchronized (this) {
      if (mIdentityFetchInProgress) return;

      long now = CurrentTimeUTC.getInMillis();
      if ((now - mLastIdentityFetchTime) < MINIMAL_IDENTITY_FETCH_DELAY) return;

      mIdentityFetchInProgress = true;
    }

    try {
      Logger.normal(this, "Requesting identities with positive score from WoT ...");
      SimpleFieldSet p1 = new SimpleFieldSet(true);
      p1.putOverwrite("Message", "GetIdentitiesByScore");
      p1.putOverwrite("Selection", "+");
      p1.putOverwrite("Context", Freetalk.WOT_CONTEXT);
      parseIdentities(sendFCPMessageBlocking(p1, null, "Identities").params, false);

      synchronized (this) {
        // We must update the fetch-time after the parsing and only if the parsing succeeded:
        // If we updated before the parsing and parsing failed or took ages (the thread sometimes
        // takes 2 hours to execute, don't ask me why)
        // then the garbage collector would delete identities.
        mLastIdentityFetchTime = CurrentTimeUTC.getInMillis();
      }
    } finally {
      synchronized (this) {
        mIdentityFetchInProgress = false;
      }
    }

    // We usually call garbageCollectIdentities() after calling this function, it updates the cache
    // already...
    // if(mShortestUniqueNicknameCacheNeedsUpdate)
    //	updateShortestUniqueNicknameCache();
  }
  /**
   * Creates a new session for the given user ID.
   *
   * <p>If a session for the given user ID already exists, it is deleted. It is not re-used to
   * ensure that parallel logins with the same user account from different computers do not work.
   *
   * @param context The ToadletContext in which the session cookie shall be stored.
   */
  public synchronized Session createSession(String userID, ToadletContext context) {
    // We must synchronize around the fetching of the time and mSessionsByID.push() because
    // mSessionsByID is no sorting data structure: It's a plain
    // LRUHashtable so to ensure that it stays sorted the operation "getTime(); push();" must be
    // atomic.
    long time = CurrentTimeUTC.getInMillis();

    removeExpiredSessions(time);

    deleteSessionByUserID(userID);

    Session session = new Session(userID, time);
    mSessionsByID.push(session.getID(), session);
    mSessionsByUserID.put(session.getUserID(), session);

    setSessionCookie(session, context);

    return session;
  }
  // TODO: Remove ID parameter and compute the ID from the URI
  public WoTIdentity(String myID, FreenetURI myRequestURI, String myNickname) {
    if (myID == null) throw new IllegalArgumentException("ID == null");
    if (myID.length() == 0) throw new IllegalArgumentException("ID.length() == 0");
    if (myRequestURI == null) throw new IllegalArgumentException("RequestURI == null");
    if (myNickname == null) throw new IllegalArgumentException("Nickname == null");

    try {
      validateNickname(myNickname);
    } catch (InvalidParameterException e) {
      throw new IllegalArgumentException(e);
    }

    mID = myID;
    mRequestURI = myRequestURI;
    mNickname = myNickname;
    mLastReceivedFromWoT = CurrentTimeUTC.getInMillis();

    IfNotEquals.thenThrow(
        IdentityID.construct(mID), IdentityID.constructFromURI(mRequestURI), "myID");
  }
  /** Debug function which checks whether the session LRU queue is in order; */
  private synchronized void verifyQueueOrder() {
    long previousTime = 0;

    Enumeration<Session> sessions = mSessionsByID.values();
    while (sessions.hasMoreElements()) {
      Session session = sessions.nextElement();

      if (session.getExpirationTime() < previousTime) {
        long sessionAge =
            (CurrentTimeUTC.getInMillis() - session.getExpirationTime()) / (60 * 60 * 1000);
        Logger.error(
            this,
            "Session LRU queue out of order! Found session which is "
                + sessionAge
                + " hour old: "
                + session);
        Logger.error(this, "Deleting all sessions...");

        mSessionsByID.clear();
        mSessionsByUserID.clear();
        return;
      }
    }
  }
  @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);
  }
  /** Makes the list of Identities known by the tree owner. */
  private void makeKnownIdentitiesList() {

    String nickFilter = mRequest.getPartAsStringFailsafe("nickfilter", 100).trim();
    String sortBy =
        mRequest.isPartSet("sortby")
            ? mRequest.getPartAsStringFailsafe("sortby", 100).trim()
            : "Nickname";
    String sortType =
        mRequest.isPartSet("sorttype")
            ? mRequest.getPartAsStringFailsafe("sorttype", 100).trim()
            : "Ascending";

    int page =
        mRequest.isPartSet("page")
            ? Integer.parseInt(
                mRequest.getPartAsStringFailsafe(
                    "page", Integer.toString(Integer.MAX_VALUE).length()))
            : 0;
    page = page - 1; // What we get passed is the user-friendly page number counting from 1, not 0.
    page = Math.max(0, page); // In case no page part was set, it would be -1

    HTMLNode knownIdentitiesBox =
        addContentBox(l10n().getString("KnownIdentitiesPage.KnownIdentities.Header"));
    knownIdentitiesBox =
        pr.addFormChild(knownIdentitiesBox, uri.toString(), "Filters").addChild("p");
    knownIdentitiesBox.addChild(
        "input",
        new String[] {"type", "name", "value"},
        new String[] {"hidden", "page", Integer.toString(page + 1)});

    InfoboxNode filtersBoxNode =
        getContentBox(l10n().getString("KnownIdentitiesPage.FiltersAndSorting.Header"));

    { // Filters box
      knownIdentitiesBox.addChild(filtersBoxNode.outer);
      HTMLNode filtersBox = filtersBoxNode.content;
      filtersBox.addChild(
          "#",
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.ShowOnlyNicksContaining")
              + " : ");
      filtersBox.addChild(
          "input",
          new String[] {"type", "size", "name", "value"},
          new String[] {"text", "15", "nickfilter", nickFilter});

      filtersBox.addChild(
          "#",
          " " + l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy") + " : ");
      HTMLNode option =
          filtersBox.addChild(
              "select", new String[] {"name", "id"}, new String[] {"sortby", "sortby"});
      TreeMap<String, String> options = new TreeMap<String, String>();
      options.put(
          SortBy.Edition.toString(),
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.Edition"));
      options.put(
          SortBy.Nickname.toString(),
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.Nickname"));
      options.put(
          SortBy.Score.toString(),
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.Score"));
      options.put(
          SortBy.LocalTrust.toString(),
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.LocalTrust"));
      for (String e : options.keySet()) {
        HTMLNode newOption = option.addChild("option", "value", e, options.get(e));
        if (e.equals(sortBy)) {
          newOption.addAttribute("selected", "selected");
        }
      }

      option =
          filtersBox.addChild(
              "select", new String[] {"name", "id"}, new String[] {"sorttype", "sorttype"});
      options = new TreeMap<String, String>();
      options.put(
          "Ascending",
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.Ascending"));
      options.put(
          "Descending",
          l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.Descending"));
      for (String e : options.keySet()) {
        HTMLNode newOption = option.addChild("option", "value", e, options.get(e));
        if (e.equals(sortType)) {
          newOption.addAttribute("selected", "selected");
        }
      }

      filtersBox.addChild(
          "input",
          new String[] {"type", "value"},
          new String[] {
            "submit",
            l10n().getString("KnownIdentitiesPage.FiltersAndSorting.SortIdentitiesBy.SubmitButton")
          });
    }

    // Display the list of known identities
    HTMLNode identitiesTable = knownIdentitiesBox.addChild("table", "border", "0");
    identitiesTable.addChild(getKnownIdentitiesListTableHeader());

    WebOfTrust.SortOrder sortInstruction = WebOfTrust.SortOrder.valueOf("By" + sortBy + sortType);

    synchronized (mWebOfTrust) {
      long currentTime = CurrentTimeUTC.getInMillis();
      int indexOfFirstIdentity = page * IDENTITIES_PER_PAGE;

      // Re-query it instead of using mLoggedInOwnIdentity because mLoggedInOwnIdentity is a
      // clone() and thus will not work with database queries on the WebOfTrust.
      // TODO: Performance: This can be removed once the TODO at
      // WebPageImpl.getLoggedInOwnIdentityFromHTTPSession() of not cloning the
      // OwnIdentity has been been resolved.
      final OwnIdentity ownId;
      try {
        ownId = mWebOfTrust.getOwnIdentityByID(mLoggedInOwnIdentity.getID());
      } catch (UnknownIdentityException e) {
        new ErrorPage(mToadlet, mRequest, mContext, e).addToPage(this);
        return;
      }

      ObjectSet<Identity> allIdentities =
          mWebOfTrust.getAllIdentitiesFilteredAndSorted(ownId, nickFilter, sortInstruction);

      Iterator<Identity> identities;
      try {
        identities = allIdentities.listIterator(indexOfFirstIdentity);
      } catch (IndexOutOfBoundsException e) {
        // The user supplied a higher page index than there are pages. This can happen when the
        // user changes the search filters while not being on the first page.
        // We fall back to displaying the last page.
        // Notice: We intentionally do not prevent listIterator() from throwing by checking
        // the index for validity before calling listIterator(). This is because we would need
        // to call allIdentities.size() to check the index. This would force the database to
        // compute the full result set even though we only need the results up to the current
        // page if we are not on the last page.
        page = getPageCount(allIdentities.size()) - 1;
        indexOfFirstIdentity = page * IDENTITIES_PER_PAGE;

        // TODO: Performance: Don't re-query this from the database once the issue which caused
        // this workaround is fixed: https://bugs.freenetproject.org/view.php?id=6646
        allIdentities =
            mWebOfTrust.getAllIdentitiesFilteredAndSorted(ownId, nickFilter, sortInstruction);

        identities = allIdentities.listIterator(indexOfFirstIdentity);
      }

      for (int displayed = 0;
          displayed < IDENTITIES_PER_PAGE && identities.hasNext();
          ++displayed) {
        final Identity id = identities.next();

        if (id == ownId) continue;

        HTMLNode row = identitiesTable.addChild("tr");

        // NickName
        HTMLNode nameLink =
            row.addChild(
                    "td",
                    new String[] {"title", "style"},
                    new String[] {id.getRequestURI().toString(), "cursor: help;"})
                .addChild("a", "href", IdentityPage.getURI(mWebInterface, id.getID()).toString());

        String nickName = id.getNickname();

        if (nickName != null) {
          nameLink.addChild("#", nickName + "@" + id.getID().substring(0, 5) + "...");
        } else
          nameLink
              .addChild("span", "class", "alert-error")
              .addChild(
                  "#",
                  l10n()
                      .getString(
                          "KnownIdentitiesPage.KnownIdentities.Table.NicknameNotDownloadedYet"));

        // Added date
        row.addChild(
            "td",
            CommonWebUtils.formatTimeDelta(currentTime - id.getAddedDate().getTime(), l10n()));

        // Last fetched date
        Date lastFetched = id.getLastFetchedDate();
        if (!lastFetched.equals(new Date(0)))
          row.addChild(
              "td", CommonWebUtils.formatTimeDelta(currentTime - lastFetched.getTime(), l10n()));
        else row.addChild("td", l10n().getString("Common.Never"));

        // Publish TrustList
        row.addChild(
            "td",
            new String[] {"align"},
            new String[] {"center"},
            id.doesPublishTrustList()
                ? l10n().getString("Common.Yes")
                : l10n().getString("Common.No"));

        // Score
        try {
          final Score score = mWebOfTrust.getScore(ownId, id);
          final int scoreValue = score.getScore();
          final int rank = score.getRank();

          row.addChild(
              "td",
              new String[] {"align", "style"},
              new String[] {
                "center", "background-color:" + KnownIdentitiesPage.getTrustColor(scoreValue) + ";"
              },
              Integer.toString(scoreValue)
                  + " ("
                  + (rank != Integer.MAX_VALUE
                      ? rank
                      : l10n().getString("KnownIdentitiesPage.KnownIdentities.Table.InfiniteRank"))
                  + ")");
        } catch (NotInTrustTreeException e) {
          // This only happen with identities added manually by the user
          row.addChild("td", l10n().getString("KnownIdentitiesPage.KnownIdentities.Table.NoScore"));
        }

        // Own Trust
        row.addChild(getReceivedTrustCell(ownId, id));

        // Checkbox
        row.addChild(getSetTrustCell(id));

        // Nb Trusters
        // TODO: Do a direct link to the received-trusts part of the linked page
        HTMLNode trustersCell = row.addChild("td", new String[] {"align"}, new String[] {"center"});
        trustersCell.addChild(
            new HTMLNode(
                "a",
                "href",
                IdentityPage.getURI(mWebInterface, id.getID()).toString(),
                Long.toString(mWebOfTrust.getReceivedTrusts(id).size())));

        // Nb Trustees
        // TODO: Do a direct link to the given-trusts part of the linked page
        HTMLNode trusteesCell = row.addChild("td", new String[] {"align"}, new String[] {"center"});
        trusteesCell.addChild(
            new HTMLNode(
                "a",
                "href",
                IdentityPage.getURI(mWebInterface, id.getID()).toString(),
                Long.toString(mWebOfTrust.getGivenTrusts(id).size())));

        // TODO: Show in advanced mode only once someone finally fixes the "Switch to advanced mode"
        // link on FProxy to work on ALL pages.

        row.addChild("td", "align", "center", Long.toString(id.getEdition()));

        row.addChild("td", "align", "center", Long.toString(id.getLatestEditionHint()));
      }
      identitiesTable.addChild(getKnownIdentitiesListTableHeader());
      knownIdentitiesBox.addChild(getKnownIdentitiesListPageLinks(page, allIdentities.size()));
    }
  }