private void migrateUser(
      final long fromDirectoryId,
      final long toDirectoryId,
      final String remoteUser,
      final User user,
      final AtomicLong migratedCount)
      throws Exception {
    if (!user.getName().equalsIgnoreCase(remoteUser)) {
      UserWithAttributes userWithAttributes =
          directoryManager.findUserWithAttributesByName(fromDirectoryId, user.getName());
      try {
        final UserTemplate newUser = new UserTemplate(user);
        newUser.setDirectoryId(toDirectoryId);
        directoryManager.addUser(
            toDirectoryId, newUser, new PasswordCredential(generatePassword()));
      } catch (InvalidUserException e) {
        // That's fine just go on to the next user.  Don't copy the groups.
        return;
      }
      // Migrate attributes
      Set<String> keys = userWithAttributes.getKeys();
      Map<String, Set<String>> attributes = new HashMap<String, Set<String>>();
      for (String key : keys) {
        Set<String> values = userWithAttributes.getValues(key);
        attributes.put(key, values);
      }
      directoryManager.storeUserAttributes(toDirectoryId, user.getName(), attributes);

      MembershipQuery<Group> groupQuery =
          QueryBuilder.queryFor(Group.class, EntityDescriptor.group())
              .parentsOf(EntityDescriptor.user())
              .withName(user.getName())
              .returningAtMost(EntityQuery.ALL_RESULTS);
      List<Group> groups =
          directoryManager.searchDirectGroupRelationships(fromDirectoryId, groupQuery);
      for (Group group : groups) {
        // We may need to add the group first
        try {
          directoryManager.findGroupByName(toDirectoryId, group.getName());
        } catch (GroupNotFoundException ex) {
          final GroupTemplate newGroup = new GroupTemplate(group);
          newGroup.setDirectoryId(toDirectoryId);
          directoryManager.addGroup(toDirectoryId, newGroup);
        }
        directoryManager.addUserToGroup(toDirectoryId, user.getName(), group.getName());
        directoryManager.removeUserFromGroup(fromDirectoryId, user.getName(), group.getName());
      }
      directoryManager.removeUser(fromDirectoryId, user.getName());
      migratedCount.addAndGet(1);
    }
  }
  /**
   * Creates a {@link User} object containing the information in the {@link Attributes} object.
   *
   * @param directoryAttributes The directory-specific {Attributes} object to take the values from
   * @return A populated {User} object.
   */
  public UserTemplateWithAttributes mapUserFromAttributes(Attributes directoryAttributes)
      throws NamingException {
    if (directoryAttributes == null) {
      throw new UncategorizedLdapException("Cannot map from null attributes");
    }

    String username = getUsernameFromAttributes(directoryAttributes);

    UserTemplate user = new UserTemplate(username, directoryId);

    // active
    user.setActive(getUserActiveFromAttribute(directoryAttributes));

    // email address
    user.setEmailAddress(StringUtils.defaultString(getUserEmailFromAttribute(directoryAttributes)));

    // first (given) name
    user.setFirstName(getUserFirstNameFromAttribute(directoryAttributes));

    // surname
    user.setLastName(getUserLastNameFromAttribute(directoryAttributes));

    // display name
    user.setDisplayName(getUserDisplayNameFromAttribute(directoryAttributes));

    // pre-populate user names (first name, last name, display name may need to be constructed)
    User prepopulatedUser = UserUtils.populateNames(user);

    return UserTemplateWithAttributes.ofUserWithNoAttributes(prepopulatedUser);
  }