protected ModelAndView onSubmit(
      HttpServletRequest request,
      HttpServletResponse response,
      Object command,
      BindException errors)
      throws Exception {
    String remoteUser = userManager.getRemoteUsername(request);

    MigrateDirectoryUsersCommand migrateUsersCommand = (MigrateDirectoryUsersCommand) command;

    migrateUsers(
        migrateUsersCommand.getFromDirectoryId(),
        migrateUsersCommand.getToDirectoryId(),
        remoteUser,
        migrateUsersCommand,
        errors);

    if (errors.hasErrors()) {
      return showForm(request, response, errors, referenceData(request));
    }
    return showForm(request, response, errors, referenceData(request));
  }
  private void migrateUsers(
      final long fromDirectoryId,
      final long toDirectoryId,
      final String remoteUser,
      final MigrateDirectoryUsersCommand migrateUsersCommand,
      final BindException errors) {
    // Check the to & from directories

    Directory from = validateDirectory(fromDirectoryId, errors, "fromDirectoryId");
    Directory to = validateDirectory(toDirectoryId, errors, "toDirectoryId");
    if (to != null && to.equals(from)) {
      errors.addError(
          new FieldError(
              "migration",
              "toDirectoryId",
              i18nResolver.getText("embedded.crowd.directory.migrate.users.field.directory.same")));
    }
    if (errors.hasErrors()) {
      return;
    }

    setDirectoryEnabled(from, false);
    setDirectoryEnabled(to, false);

    // Copy
    SearchRestriction restriction = NullRestrictionImpl.INSTANCE;
    UserQuery<User> query = new UserQuery<User>(User.class, restriction, 0, -1);
    try {
      // TODO: Sucking all users in like this may be problematic for Confluence.  If planning to
      // enable this
      // TODO: feature for other products make sure the performance impacts are understood.
      List<User> users = directoryManager.searchUsers(fromDirectoryId, query);
      final AtomicLong migratedCount = new AtomicLong(0);
      for (Iterator<User> iter = users.iterator(); iter.hasNext(); ) {
        final User user = iter.next();
        transactionTemplate.execute(
            new TransactionCallback() {
              public Object doInTransaction() {
                try {
                  migrateUser(fromDirectoryId, toDirectoryId, remoteUser, user, migratedCount);
                } catch (Exception e) {
                  throw new RuntimeException(e);
                }
                return null;
              }
            });
      }
      migrateUsersCommand.setTestSuccessful(true);
      migrateUsersCommand.setTotalCount(users.size());
      migrateUsersCommand.setMigratedCount(migratedCount.get());
    } catch (Exception e) {
      log.error("User migration failed", e);
      errors.addError(
          new ObjectError(
              "migration",
              i18nResolver.getText(
                  "embedded.crowd.directory.migrate.users.error",
                  htmlEncoder.encode(e.getMessage()))));
    }

    // Enable both directories
    setDirectoryEnabled(from, true);
    setDirectoryEnabled(to, true);
  }