You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2016/02/05 23:42:41 UTC

[1/9] usergrid git commit: Make duplicate admin repair tool testable, add dry-run feature and RxJava. Also, make DupOrgRepair case-insensitive

Repository: usergrid
Updated Branches:
  refs/heads/1.x e0be7dda2 -> 14d6cdf97


http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/ExportImportAdminsTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/ExportImportAdminsTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/ExportImportAdminsTest.java
index 9cce040..260c7ce 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/ExportImportAdminsTest.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/ExportImportAdminsTest.java
@@ -52,6 +52,7 @@ public class ExportImportAdminsTest {
     @ClassRule
     public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource );
 
+
     @org.junit.Test
     public void testExportUserAndOrg() throws Exception {
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
new file mode 100644
index 0000000..e8323e4
--- /dev/null
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
@@ -0,0 +1,215 @@
+package org.apache.usergrid.tools;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import rx.*;
+
+import java.util.*;
+import java.util.Observable;
+
+/**
+ * Mock manager implementation for mockTesting.
+ */
+class MockUserOrgManager implements UserOrgInterface {
+
+    SetMultimap<Org, OrgUser> usersByOrg = HashMultimap.create();
+
+    SetMultimap<OrgUser, Org> orgsByUser = HashMultimap.create();
+
+    SetMultimap<Org, UUID> appsByOrg = HashMultimap.create();
+
+    Map<UUID, Org> orgsById = new HashMap<UUID, Org>();
+
+    Map<UUID, OrgUser> usersById = new HashMap<UUID, OrgUser>();
+
+    /** represents "index" of users by username */
+    Map<String, OrgUser> usersByUsername = new HashMap<String, OrgUser>();
+
+    /** represents "index" of users by email */
+    Map<String, OrgUser> usersByEmail = new HashMap<String, OrgUser>();
+
+    
+    /**
+     * Populate manager with orgs and users.
+     * Will create a number of orgs and a duplicate for each.
+     *
+     * @param numOrgs One half of the number of orgs to create.
+     */
+    public MockUserOrgManager(int numOrgs) throws Exception {
+        
+        for (int i = 0; i < numOrgs; i++) {
+
+            // create a pair of duplicate orgs 
+            
+            // org1 is the original and the oldest one, with capital letters
+            Org org1 = new Org( UUID.randomUUID(), "OrG_" + i );
+            orgsById.put( org1.getId(), org1 );
+            pause(100);
+            
+            // org2 is duplicate, the newest one
+            Org org2 = new Org( UUID.randomUUID(), "org_" + i );
+            orgsById.put( org2.getId(), org2 );
+
+            // create three users A, B and C
+            
+            String base = "user_" + i;
+            OrgUser usera = new OrgUser( UUID.randomUUID(), base + "_a", base + "_a@example.com" );
+            OrgUser userb = new OrgUser( UUID.randomUUID(), base + "_b", base + "_b@example.com" );
+            OrgUser userc = new OrgUser( UUID.randomUUID(), base + "_c", base + "_c@example.com" );
+
+            // org1 gets users A and B
+            addUserToOrg( usera, org1 );
+            addUserToOrg( userb, org1 );
+
+            // org2 gets users C 
+            addUserToOrg( userc, org2 );
+
+            // add some apps to the orgs, org1 gets 2 apps
+            addAppToOrg( UUID.randomUUID(), org1 );
+            addAppToOrg( UUID.randomUUID(), org1 );
+           
+            // org2 gets 1 app
+            addAppToOrg( UUID.randomUUID(), org2 );
+        }
+    }
+    
+    void pause( long timems ) {
+        try { Thread.sleep( timems ); } catch (InterruptedException intentionallyIgnored) {}
+    }
+
+    @Override
+    public rx.Observable<Org> getOrgs() throws Exception {
+        return rx.Observable.from( usersByOrg.keySet() );
+    }
+
+    @Override
+    public rx.Observable<OrgUser> getUsers() throws Exception {
+        return rx.Observable.from( orgsByUser.keySet() );
+    }
+
+    @Override
+    public Set<Org> getUsersOrgs(OrgUser user) {
+        return orgsByUser.get( user );
+    }
+
+    @Override
+    public void removeOrg(Org keeper, Org duplicate) throws Exception {
+        Set<OrgUser> users = usersByOrg.get( duplicate );
+        for (OrgUser user : users) {
+            Set<Org> userOrgs = orgsByUser.get( user );
+            userOrgs.remove( duplicate );
+        }
+        usersByOrg.removeAll( duplicate );
+    }
+
+    @Override
+    public Set<OrgUser> getOrgUsers(Org org) throws Exception {
+        return usersByOrg.get( org );
+    }
+
+    @Override
+    public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
+        
+        Set<OrgUser> orgUsers = usersByOrg.get( org );
+        orgUsers.remove( user );
+        
+        Set<Org> usersOrgs = orgsByUser.get( user );
+        usersOrgs.remove( org );
+    }
+
+    @Override
+    public void addUserToOrg(OrgUser user, Org org) throws Exception {
+        
+        Set<Org> usersOrgs = orgsByUser.get( user );
+        usersOrgs.add( org );
+        
+        Set<OrgUser> orgsUsers = usersByOrg.get( org );
+        orgsUsers.add( user );
+    }
+
+    @Override
+    public Set<UUID> getOrgApps(Org org) {
+        return appsByOrg.get( org );
+    }
+
+    @Override
+    public void removeAppFromOrg(UUID appId, Org org) throws Exception {
+        Set<UUID> apps = appsByOrg.get( org );
+        apps.remove( appId );
+    }
+
+    @Override
+    public void addAppToOrg(UUID appId, Org org) throws Exception {
+        Set<UUID> apps = appsByOrg.get( org );
+        apps.add(appId); 
+    }
+
+    @Override
+    public void logDuplicates(Map<String, Set<Org>> duplicatesByName) {
+
+        for (String orgName : duplicatesByName.keySet()) {
+            Set<Org> orgs = duplicatesByName.get( orgName );
+            for (Org org : orgs) {
+                DuplicateAdminUserRepairTest.logger.info( "Duplicate org {}:{}", orgName, org.getId() );
+            }
+        }
+    }
+
+    @Override
+    public Org getOrg(UUID uuid) throws Exception {
+        return orgsById.get(uuid); 
+    }
+
+    @Override
+    public OrgUser getOrgUser(UUID id) throws Exception {
+        return usersById.get(id);
+    }
+
+    @Override
+    public OrgUser lookupOrgUserByUsername(String username) {
+        return usersByUsername.get(username);
+    }
+
+    @Override
+    public OrgUser lookupOrgUserByEmail(String email) {
+        return usersByEmail.get(email);
+    }
+
+    @Override
+    public void removeOrgUser(OrgUser orgUser) throws Exception {
+
+        usersById.remove( orgUser.getId() );
+        usersByUsername.remove( orgUser.getId() );
+        usersByEmail.remove( orgUser.getId() );
+        
+        Set<Org> orgs = orgsByUser.get( orgUser );
+        for ( Org org : orgs ) {
+            removeUserFromOrg( orgUser, org );
+        }
+    }
+
+    @Override
+    public void updateOrgUser(OrgUser keeper) throws Exception  {
+        // ensure 'keeper' user is the one in the indexes
+        usersById.put(       keeper.getId(), keeper );
+        usersByUsername.put( keeper.getUsername(), keeper );
+        usersByEmail.put(    keeper.getEmail(), keeper );
+    }
+
+    @Override
+    public void setOrgUserName(OrgUser user, String newUserName) throws Exception  {
+        user.setUsername( newUserName );
+        updateOrgUser( user ); // re-index user
+    }
+
+    // implemented for testing only
+    OrgUser createOrgUser(UUID id, String name, String email) {
+        OrgUser user = new OrgUser( id, name, email );
+        usersById.put( user.getId(), user );
+        usersByUsername.put( user.getUsername(), user );
+        usersByEmail.put( user.getEmail(), user );
+        return user;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/resources/log4j.properties b/stack/tools/src/test/resources/log4j.properties
new file mode 100644
index 0000000..def47b4
--- /dev/null
+++ b/stack/tools/src/test/resources/log4j.properties
@@ -0,0 +1,47 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# for production, you should probably set the root to INFO
+# and the pattern to %c instead of %l.  (%l is slower.)
+
+# output messages into a rolling log file as well as stdout
+log4j.rootLogger=ERROR,stdout
+
+# stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+#log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p (%t) [%c] - %m%n
+
+log4j.logger.org.apache.usergrid=INFO
+log4j.logger.org.apache.usergrid.tools=DEBUG
+
+log4j.logger.org.apache.usergrid.management.cassandra=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.DB=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.BATCH=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.EntityManagerFactoryImpl=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.DaoUtils=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.EntityManagerImpl=WARN
+log4j.logger.org.apache.usergrid.persistence.cassandra.ConnectionRefImpl=WARN
+log4j.logger.me.prettyprint.cassandra.hector.TimingLogger=WARN
+log4j.logger.org.apache.usergrid.rest.security.AllowAjaxFilter=WARN
+log4j.logger.me.prettyprint.hector.api.beans.AbstractComposite=ERROR
+#log4j.logger.org.apache.usergrid.locking.singlenode.SingleNodeLockManagerImpl=DEBUG, stdout
+
+log4j.logger.org.apache.usergrid.persistence.hector.CountingMutator=INFO
+
+#log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG, stdout
+


[6/9] usergrid git commit: Change test to verify that dry-run uses dry-run manager, plus a logging fix.

Posted by sn...@apache.org.
Change test to verify that dry-run uses dry-run manager, plus a logging fix.


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/0316b57f
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/0316b57f
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/0316b57f

Branch: refs/heads/1.x
Commit: 0316b57fa41829ce06e866d160b9197df27544c5
Parents: ceb8107
Author: Dave Johnson <sn...@apache.org>
Authored: Tue Jan 12 08:03:42 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Tue Jan 12 08:03:42 2016 -0500

----------------------------------------------------------------------
 .../java/org/apache/usergrid/tools/DuplicateAdminRepair.java | 3 +--
 .../org/apache/usergrid/tools/DuplicateAdminRepairTest.java  | 8 +++++---
 2 files changed, 6 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/0316b57f/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
index f38e3d6..9909918 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
@@ -275,13 +275,12 @@ public class DuplicateAdminRepair extends ToolBase {
             }
         }
 
-        logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
-
         if ( dryRun ) {
             logger.info( "Would remove user {}:{}", new Object[]{
                     sourceUser.getUsername(), sourceUser.getId() } );
             
         } else {
+            logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
             manager.removeOrgUser( sourceUser );
         }
     }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/0316b57f/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
index e5e9d43..1583468 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
@@ -132,10 +132,10 @@ public class DuplicateAdminRepairTest {
 
     
     /**
-     * Smoke test: does "real" manager run without throwing exceptions?
+     * Verify that dry-run manager will be used when dry-run specified.
      */
     @org.junit.Test
-    public void testManagerNoDups() throws Exception {
+    public void testManagerDryRunNoDups() throws Exception {
 
         // create two orgs each with owning user
 
@@ -156,7 +156,9 @@ public class DuplicateAdminRepairTest {
 
         DuplicateAdminRepair dor = new DuplicateAdminRepair();
 
-        dor.startTool( new String[]{}, false );  // false means do not call System.exit()
+        dor.startTool( new String[] { "-dryrun", "true" }, false );  // false means do not call System.exit()
+        
+        assertTrue( DryRunUserOrgManager.class.isAssignableFrom( dor.manager.getClass() ) );
 
         assertTrue( true ); // we're happy if we get to this point
     }


[5/9] usergrid git commit: Additional tests for DuplicateAdminRepair, remove old DupAdminRepair.

Posted by sn...@apache.org.
Additional tests for DuplicateAdminRepair, remove old DupAdminRepair.


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/ceb81076
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/ceb81076
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/ceb81076

Branch: refs/heads/1.x
Commit: ceb81076520db67e88f6c7a0e0934e39703309e7
Parents: e525f4f
Author: Dave Johnson <sn...@apache.org>
Authored: Mon Jan 11 18:08:54 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Mon Jan 11 18:08:54 2016 -0500

----------------------------------------------------------------------
 .../apache/usergrid/tools/DupAdminRepair.java   | 311 -------------------
 .../usergrid/tools/DuplicateAdminRepair.java    | 289 +++++++++++++++++
 .../tools/DuplicateAdminUserRepair.java         | 289 -----------------
 .../tools/DuplicateAdminRepairTest.java         | 301 ++++++++++++++++++
 .../tools/DuplicateAdminUserRepairTest.java     | 301 ------------------
 .../usergrid/tools/MockUserOrgManager.java      |   2 +-
 6 files changed, 591 insertions(+), 902 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/main/java/org/apache/usergrid/tools/DupAdminRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DupAdminRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DupAdminRepair.java
deleted file mode 100644
index 45929e8..0000000
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DupAdminRepair.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.usergrid.tools;
-
-
-import java.io.FileWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.usergrid.management.OrganizationInfo;
-import org.apache.usergrid.management.UserInfo;
-import org.apache.usergrid.persistence.Entity;
-import org.apache.usergrid.persistence.EntityManager;
-import org.apache.usergrid.persistence.Query;
-import org.apache.usergrid.persistence.Results;
-import org.apache.usergrid.persistence.SimpleEntityRef;
-import org.apache.usergrid.persistence.cassandra.CassandraService;
-import org.apache.usergrid.persistence.entities.Application;
-import org.apache.usergrid.persistence.entities.User;
-import org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
-import org.apache.usergrid.utils.JsonUtils;
-
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-
-import static org.apache.usergrid.persistence.cassandra.CassandraService.MANAGEMENT_APPLICATION_ID;
-
-
-/**
- * This is a utility to load all entities in an application and re-save them, this forces the secondary indexing to be
- * updated.
- *
- * @author tnine
- */
-public class DupAdminRepair extends ExportingToolBase {
-
-    /**
-     *
-     */
-    private static final int PAGE_SIZE = 100;
-
-    private static final Logger logger = LoggerFactory.getLogger( DupAdminRepair.class );
-
-
-    @Override
-    @SuppressWarnings("static-access")
-    public Options createOptions() {
-
-        Option hostOption =
-                OptionBuilder.withArgName( "host" ).hasArg().isRequired( true ).withDescription( "Cassandra host" )
-                             .create( "host" );
-
-        Option outputOption =
-                OptionBuilder.withArgName( "output" ).hasArg().isRequired( true ).withDescription( "Cassandra host" )
-                             .create( "output" );
-
-        Options options = new Options();
-        options.addOption( hostOption );
-        options.addOption( outputOption );
-
-        return options;
-    }
-
-
-    /*
-     * (non-Javadoc)
-     *
-     * @see
-     * org.apache.usergrid.tools.ToolBase#runTool(org.apache.commons.cli.CommandLine)
-     */
-    @Override
-    public void runTool( CommandLine line ) throws Exception {
-        String outputDir = line.getOptionValue( "output" );
-
-        String emailsDir = String.format( "%s/emails", outputDir );
-        String usernamesDir = String.format( "%s/usernames", outputDir );
-        createDir( emailsDir );
-        createDir( usernamesDir );
-
-        startSpring();
-
-        logger.info( "Starting crawl of all admins" );
-
-        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-        Application app = em.getApplication();
-
-        // search for all orgs
-
-        Query query = new Query();
-        query.setLimit( PAGE_SIZE );
-        Results r = null;
-
-        Multimap<String, UUID> emails = HashMultimap.create();
-        Multimap<String, UUID> usernames = HashMultimap.create();
-        do {
-
-            r = em.searchCollection( app, "users", query );
-
-            for ( Entity entity : r.getEntities() ) {
-                emails.put( entity.getProperty( "email" ).toString().toLowerCase(), entity.getUuid() );
-                usernames.put( entity.getProperty( "username" ).toString().toLowerCase(), entity.getUuid() );
-            }
-
-            query.setCursor( r.getCursor() );
-
-            logger.info( "Searching next page" );
-        }
-        while ( r != null && r.size() == PAGE_SIZE );
-
-        // now go through and print out duplicate emails
-
-        for ( String username : usernames.keySet() ) {
-            Collection<UUID> ids = usernames.get( username );
-
-            if ( ids.size() > 1 ) {
-                logger.info( "Found multiple users with the username {}", username );
-
-                // force the username to be reset to the user's email
-                resolveUsernameConflicts( usernamesDir, username, ids );
-            }
-        }
-
-        for ( String email : emails.keySet() ) {
-            Collection<UUID> ids = emails.get( email );
-
-            if ( ids.size() > 1 ) {
-                // get the admin the same way as the rest tier, this way the OTHER
-                // admins will be removed
-                UserInfo targetUser = managementService.getAdminUserByEmail( email );
-
-                if ( targetUser == null ) {
-
-                    List<UUID> tempIds = new ArrayList<UUID>( ids );
-                    Collections.sort( tempIds );
-
-                    UUID toLoad = tempIds.get( 0 );
-
-                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad );
-                    targetUser = managementService.getAdminUserByUuid( toLoad );
-
-                    ids.remove( toLoad );
-                }
-
-                UUID targetId = targetUser.getUuid();
-
-                ids.remove( targetId );
-
-                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, targetId );
-
-                FileWriter file = new FileWriter( String.format( "%s/%s.all", emailsDir, email ) );
-
-                Map<String, Object> userOrganizationData = managementService.getAdminUserOrganizationData( targetId );
-
-                file.write( JsonUtils.mapToFormattedJsonString( userOrganizationData ) );
-
-                for ( UUID id : ids ) {
-
-                    userOrganizationData = managementService.getAdminUserOrganizationData( id );
-
-                    file.write( JsonUtils.mapToFormattedJsonString( userOrganizationData ) );
-
-                    file.write( "\n\n" );
-
-                    mergeAdmins( emailsDir, id, targetId );
-                }
-
-                file.flush();
-                file.close();
-
-                // force the index update after all other admins have been merged
-                logger.info( "Forcing re-index of admin with email {} and id {}", email, targetId );
-                User targetUserEntity = em.get( targetUser.getUuid(), User.class );
-                em.update( targetUserEntity );
-
-                FileWriter merged = new FileWriter( String.format( "%s/%s.merged", emailsDir, email ) );
-
-                userOrganizationData = managementService.getAdminUserOrganizationData( targetUser.getUuid() );
-
-                merged.write( JsonUtils.mapToFormattedJsonString( userOrganizationData ) );
-                merged.flush();
-                merged.close();
-            }
-        }
-
-        logger.info( "Repair complete" );
-    }
-
-
-    /**
-     * When our usernames are equal, we need to check if our emails are equal. If they're not, we need to change the one
-     * that DOES NOT get returned on a lookup by username
-     */
-    private void resolveUsernameConflicts( String targetDir, String userName, Collection<UUID> ids ) throws Exception {
-        // lookup the admin id
-        UserInfo existing = managementService.getAdminUserByUsername( userName );
-
-        if ( existing == null ) {
-            logger.warn( "Could not determine an admin for colliding username '{}'.  Skipping", userName );
-            return;
-        }
-
-        ids.remove( existing.getUuid() );
-
-        boolean collision = false;
-
-        EntityManager em = emf.getEntityManager( MANAGEMENT_APPLICATION_ID );
-
-        for ( UUID id : ids ) {
-            UserInfo other = managementService.getAdminUserByUuid( id );
-
-            // same username and email, these will be merged later in the process,
-            // skip it
-            if ( other != null && other.getEmail() != null && other.getEmail().equals( existing.getEmail() ) ) {
-                logger.info(
-                        "Users with the same username '{}' have the same email '{}'. This will be resolved later in "
-                                + "the process, skipping", userName, existing.getEmail() );
-                continue;
-            }
-
-            // if we get here, the emails do not match, but the usernames do. Force
-            // both usernames to emails
-            collision = true;
-
-            setUserName( em, other, other.getEmail() );
-        }
-
-        if ( collision ) {
-            setUserName( em, existing, existing.getEmail() );
-        }
-    }
-
-
-    /** Set the username to the one provided, if we can't due to duplicate property issues, we fall back to user+uuid */
-    private void setUserName( EntityManager em, UserInfo other, String newUserName ) throws Exception {
-        logger.info( "Setting username to {} for user with username {} and id {}", new Object[] {
-                newUserName, other.getUsername(), other.getUuid()
-        } );
-
-        try {
-            em.setProperty( new SimpleEntityRef( "user", other.getUuid() ), "username", newUserName, true );
-        }
-        catch ( DuplicateUniquePropertyExistsException e ) {
-            logger.warn( "More than 1 user has the username of {}.  Setting the username to their username+UUID as a "
-                    + "fallback", newUserName );
-
-            setUserName( em, other, String.format( "%s-%s", other.getUsername(), other.getUuid() ) );
-        }
-    }
-
-
-    /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */
-    private void mergeAdmins( String targetDir, UUID sourceId, UUID targetId ) throws Exception {
-
-        EntityManager em = emf.getEntityManager( MANAGEMENT_APPLICATION_ID );
-
-        User sourceUser = em.get( sourceId, User.class );
-
-        // may have already been deleted, do nothing
-        if ( sourceUser == null ) {
-            logger.warn( "Source admin with uuid {} does not exist in cassandra", sourceId );
-            return;
-        }
-
-        UserInfo targetUserInfo = managementService.getAdminUserByUuid( targetId );
-
-        @SuppressWarnings("unchecked") Map<String, Map<String, UUID>> sourceOrgs =
-                ( Map<String, Map<String, UUID>> ) managementService.getAdminUserOrganizationData( sourceId )
-                                                                    .get( "organizations" );
-
-        for ( String orgName : sourceOrgs.keySet() ) {
-            UUID orgId = sourceOrgs.get( orgName ).get( "uuid" );
-
-            OrganizationInfo org = managementService.getOrganizationByUuid( orgId );
-
-            logger.info( "Adding organization {} to admin with email {} and id {}",
-                    new Object[] { org.getName(), sourceUser.getEmail(), sourceUser.getUuid() } );
-
-            // copy it over to the target admin
-            managementService.addAdminUserToOrganization( targetUserInfo, org, false );
-        }
-
-        logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getUuid() );
-
-        em.delete( sourceUser );
-    }
-}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
new file mode 100644
index 0000000..f38e3d6
--- /dev/null
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.usergrid.management.ManagementService;
+import org.apache.usergrid.persistence.EntityManagerFactory;
+import rx.functions.Action1;
+
+import java.util.*;
+
+import static org.apache.usergrid.tools.UserOrgInterface.Org;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
+
+
+/**
+ * Find duplicate admin users, delete the one that is not indexed.
+ */
+public class DuplicateAdminRepair extends ToolBase {
+
+    UserOrgInterface           manager = null;
+    
+    static final String        THREADS_ARG_NAME = "threads"; 
+    
+    int                        threadCount = 5;
+
+    static final String        DRYRUN_ARG_NAME = "dryrun";
+
+    boolean                    dryRun = false;
+
+    Multimap<String, OrgUser>  emails = HashMultimap.create();
+    
+    Multimap<String, OrgUser>  usernames = HashMultimap.create();
+
+    boolean                    testing = false;
+
+
+    DuplicateAdminRepair() {
+        super();
+    }
+
+    DuplicateAdminRepair(EntityManagerFactory emf, ManagementService managementService ) {
+        this();
+        this.emf = emf;
+        this.managementService = managementService;
+    }
+    
+    
+    @Override
+    @SuppressWarnings("static-access")
+    public Options createOptions() {
+
+        Options options = super.createOptions();
+
+        Option dryRunOption = OptionBuilder.hasArg()
+            .withType(Boolean.TRUE)
+            .withDescription( "-" + DRYRUN_ARG_NAME + " true to print what tool would do and do not alter data.")
+            .create( DRYRUN_ARG_NAME );
+        options.addOption( dryRunOption );
+        
+        Option writeThreadsOption = OptionBuilder.hasArg()
+            .withType(0)
+            .withDescription( "Write Threads -" + THREADS_ARG_NAME )
+            .create(THREADS_ARG_NAME);
+        options.addOption( writeThreadsOption );        
+        
+        return options;
+    }
+
+    
+    public UserOrgManager createNewRepairManager() {
+        return new UserOrgManager( emf, managementService );
+    }
+   
+    
+    @Override
+    public void runTool(CommandLine line) throws Exception {
+
+        startSpring();
+        setVerbose( line );
+
+        if (StringUtils.isNotEmpty( line.getOptionValue( THREADS_ARG_NAME ) )) {
+            try {
+                threadCount = Integer.parseInt( line.getOptionValue( THREADS_ARG_NAME ) );
+            } catch (NumberFormatException nfe) {
+                logger.error( "-" + THREADS_ARG_NAME + " must be specified as an integer. Aborting..." );
+                return;
+            }
+        }
+
+        if ( StringUtils.isNotEmpty( line.getOptionValue( DRYRUN_ARG_NAME ) )) {
+            dryRun = Boolean.parseBoolean( line.getOptionValue( DRYRUN_ARG_NAME ));
+        }
+
+        if ( manager == null ) { // we use a special manager when mockTesting
+            if (dryRun) {
+                manager = new DryRunUserOrgManager( emf, managementService );
+            } else {
+                manager = new UserOrgManager( emf, managementService );
+            }
+        } 
+
+        logger.info( "DuplicateAdminUserRepair tool starting up... manager: " + manager.getClass().getSimpleName() );
+       
+        // build multi-map of users by email and users by name
+        
+        manager.getUsers().doOnNext( new Action1<OrgUser>() {
+            @Override
+            public void call( OrgUser user ) {
+
+                if (user.getUsername() == null) {
+                    logger.warn( "User {} has no username", user.getId() );
+                    return;
+                }
+                if (user.getEmail() == null) {
+                    logger.warn( "User {} has no email", user.getId() );
+                    return;
+                }
+                emails.put( user.getEmail(), user );
+                usernames.put( user.getEmail(), user );
+                
+            }
+        } ).toBlocking().lastOrDefault( null );
+
+        for ( String username : usernames.keySet() ) {
+            Collection<OrgUser> users = usernames.get( username );
+
+            if ( users.size() > 1 ) {
+                logger.info( "Found multiple users with the username {}", username );
+
+                // force the username to be reset to the user's email
+                resolveUsernameConflicts( username, users );
+            }
+        }
+
+        for ( String email : emails.keySet() ) {
+            
+            Collection<OrgUser> users = emails.get( email );
+
+            if ( users.size() > 1 ) {
+                // get the admin the same way as the rest tier, this way the OTHER
+                // admins will be removed
+                OrgUser targetUser = manager.lookupOrgUserByEmail( email );
+
+                if ( targetUser == null ) {
+
+                    List<OrgUser> tempUsers = new ArrayList<OrgUser>( users );
+                    Collections.sort( tempUsers );
+
+                    OrgUser toLoad = tempUsers.get( 0 );
+
+                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad );
+                    targetUser = toLoad;
+
+                    users.remove( toLoad );
+                }
+
+                users.remove( targetUser );
+
+                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, targetUser.getId() );
+
+                for ( OrgUser orgUser : users ) {
+                    mergeAdmins( orgUser, targetUser );
+                }
+
+                // force the index update after all other admins have been merged
+                if ( dryRun ) {
+                    logger.info("Would force re-index of 'keeper' user {}:{}", 
+                            targetUser.getUsername(), targetUser.getId());
+                } else {
+                    logger.info( "Forcing re-index of admin with email {} and id {}", email, targetUser.getId());
+                    manager.updateOrgUser( targetUser );
+                }
+            }
+        }
+
+        logger.info( "Repair complete" );
+    }
+
+
+    /**
+     * When our usernames are equal, we need to check if our emails are equal. If they're not, we need to change the one
+     * that DOES NOT get returned on a lookup by username
+     */
+    private void resolveUsernameConflicts( String userName, Collection<OrgUser> users ) throws Exception {
+        
+        // lookup the admin id
+        OrgUser existing = manager.lookupOrgUserByUsername( userName );
+
+        if ( existing == null ) {
+            logger.warn( "Could not determine an admin for colliding username '{}'.  Skipping", userName );
+            return;
+        }
+
+        users.remove( existing );
+
+        boolean collision = false;
+
+        for ( OrgUser other : users ) {
+
+            // same username and email, these will be merged later in the process,
+            // skip it
+            if ( other != null && other.getEmail() != null && other.getEmail().equals( existing.getEmail() ) ) {
+                logger.info(
+                        "Users with the same username '{}' have the same email '{}'. This will be resolved later in "
+                                + "the process, skipping", userName, existing.getEmail() );
+                continue;
+            }
+
+            // if we get here, the emails do not match, but the usernames do. Force
+            // both usernames to emails
+            collision = true;
+
+            setUserName(other, other.getEmail() );
+        }
+
+        if ( collision ) {
+            setUserName(existing, existing.getEmail() );
+        }
+    }
+
+
+    /** Set the username to the one provided, if we can't due to duplicate property issues, we fall back to user+uuid */
+    private void setUserName( OrgUser other, String newUserName ) throws Exception {
+
+        if ( dryRun ) {
+            logger.info("Would rename user {}:{} to {}", new Object[] { 
+                    other.getUsername(), other.getId(), newUserName });
+        } else {
+            logger.info( "Setting username to {} for user {}:{}", new Object[] {
+                    newUserName, other.getUsername(), other.getId() } );
+            
+            manager.setOrgUserName( other, newUserName );
+        }
+    }
+
+
+    /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */
+    private void mergeAdmins( OrgUser sourceUser, OrgUser targetUser ) throws Exception {
+
+        Set<Org> sourceOrgs = manager.getUsersOrgs( sourceUser ); 
+
+        for ( Org org : sourceOrgs ) {
+
+            if ( dryRun ) {
+                logger.info("Would add org {}:{} to user {}:{}", new Object[] {
+                        org.getName(), org.getId(), targetUser.getUsername(), targetUser.getId(), });
+
+            } else {
+                logger.info( "Adding organization {}:{} to admin with email {} and id {}",
+                    new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
+
+                // copy it over to the target admin
+                manager.addUserToOrg( targetUser, org );
+            }
+        }
+
+        logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
+
+        if ( dryRun ) {
+            logger.info( "Would remove user {}:{}", new Object[]{
+                    sourceUser.getUsername(), sourceUser.getId() } );
+            
+        } else {
+            manager.removeOrgUser( sourceUser );
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
deleted file mode 100644
index 902eefa..0000000
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.usergrid.tools;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.Option;
-import org.apache.commons.cli.OptionBuilder;
-import org.apache.commons.cli.Options;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.usergrid.management.ManagementService;
-import org.apache.usergrid.persistence.EntityManagerFactory;
-import rx.functions.Action1;
-
-import java.util.*;
-
-import static org.apache.usergrid.tools.UserOrgInterface.Org;
-import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
-
-
-/**
- * Find duplicate admin users, delete the one that is not indexed.
- */
-public class DuplicateAdminUserRepair extends ToolBase {
-
-    UserOrgInterface           manager = null;
-    
-    static final String        THREADS_ARG_NAME = "threads"; 
-    
-    int                        threadCount = 5;
-
-    static final String        DRYRUN_ARG_NAME = "dryrun";
-
-    boolean                    dryRun = false;
-
-    Multimap<String, OrgUser>  emails = HashMultimap.create();
-    
-    Multimap<String, OrgUser>  usernames = HashMultimap.create();
-
-    boolean                    testing = false;
-
-
-    DuplicateAdminUserRepair() {
-        super();
-    }
-
-    DuplicateAdminUserRepair(EntityManagerFactory emf, ManagementService managementService ) {
-        this();
-        this.emf = emf;
-        this.managementService = managementService;
-    }
-    
-    
-    @Override
-    @SuppressWarnings("static-access")
-    public Options createOptions() {
-
-        Options options = super.createOptions();
-
-        Option dryRunOption = OptionBuilder.hasArg()
-            .withType(Boolean.TRUE)
-            .withDescription( "-" + DRYRUN_ARG_NAME + " true to print what tool would do and do not alter data.")
-            .create( DRYRUN_ARG_NAME );
-        options.addOption( dryRunOption );
-        
-        Option writeThreadsOption = OptionBuilder.hasArg()
-            .withType(0)
-            .withDescription( "Write Threads -" + THREADS_ARG_NAME )
-            .create(THREADS_ARG_NAME);
-        options.addOption( writeThreadsOption );        
-        
-        return options;
-    }
-
-    
-    public UserOrgManager createNewRepairManager() {
-        return new UserOrgManager( emf, managementService );
-    }
-   
-    
-    @Override
-    public void runTool(CommandLine line) throws Exception {
-
-        startSpring();
-        setVerbose( line );
-
-        if (StringUtils.isNotEmpty( line.getOptionValue( THREADS_ARG_NAME ) )) {
-            try {
-                threadCount = Integer.parseInt( line.getOptionValue( THREADS_ARG_NAME ) );
-            } catch (NumberFormatException nfe) {
-                logger.error( "-" + THREADS_ARG_NAME + " must be specified as an integer. Aborting..." );
-                return;
-            }
-        }
-
-        if ( StringUtils.isNotEmpty( line.getOptionValue( DRYRUN_ARG_NAME ) )) {
-            dryRun = Boolean.parseBoolean( line.getOptionValue( DRYRUN_ARG_NAME ));
-        }
-
-        if ( manager == null ) { // we use a special manager when mockTesting
-            if (dryRun) {
-                manager = new DryRunUserOrgManager( emf, managementService );
-            } else {
-                manager = new UserOrgManager( emf, managementService );
-            }
-        } 
-
-        logger.info( "DuplicateAdminUserRepair tool starting up... manager: " + manager.getClass().getSimpleName() );
-       
-        // build multi-map of users by email and users by name
-        
-        manager.getUsers().doOnNext( new Action1<OrgUser>() {
-            @Override
-            public void call( OrgUser user ) {
-
-                if (user.getUsername() == null) {
-                    logger.warn( "User {} has no username", user.getId() );
-                    return;
-                }
-                if (user.getEmail() == null) {
-                    logger.warn( "User {} has no email", user.getId() );
-                    return;
-                }
-                emails.put( user.getEmail(), user );
-                usernames.put( user.getEmail(), user );
-                
-            }
-        } ).toBlocking().lastOrDefault( null );
-
-        for ( String username : usernames.keySet() ) {
-            Collection<OrgUser> users = usernames.get( username );
-
-            if ( users.size() > 1 ) {
-                logger.info( "Found multiple users with the username {}", username );
-
-                // force the username to be reset to the user's email
-                resolveUsernameConflicts( username, users );
-            }
-        }
-
-        for ( String email : emails.keySet() ) {
-            
-            Collection<OrgUser> users = emails.get( email );
-
-            if ( users.size() > 1 ) {
-                // get the admin the same way as the rest tier, this way the OTHER
-                // admins will be removed
-                OrgUser targetUser = manager.lookupOrgUserByEmail( email );
-
-                if ( targetUser == null ) {
-
-                    List<OrgUser> tempUsers = new ArrayList<OrgUser>( users );
-                    Collections.sort( tempUsers );
-
-                    OrgUser toLoad = tempUsers.get( 0 );
-
-                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad );
-                    targetUser = toLoad;
-
-                    users.remove( toLoad );
-                }
-
-                users.remove( targetUser );
-
-                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, targetUser.getId() );
-
-                for ( OrgUser orgUser : users ) {
-                    mergeAdmins( orgUser, targetUser );
-                }
-
-                // force the index update after all other admins have been merged
-                if ( dryRun ) {
-                    logger.info("Would force re-index of 'keeper' user {}:{}", 
-                            targetUser.getUsername(), targetUser.getId());
-                } else {
-                    logger.info( "Forcing re-index of admin with email {} and id {}", email, targetUser.getId());
-                    manager.updateOrgUser( targetUser );
-                }
-            }
-        }
-
-        logger.info( "Repair complete" );
-    }
-
-
-    /**
-     * When our usernames are equal, we need to check if our emails are equal. If they're not, we need to change the one
-     * that DOES NOT get returned on a lookup by username
-     */
-    private void resolveUsernameConflicts( String userName, Collection<OrgUser> users ) throws Exception {
-        
-        // lookup the admin id
-        OrgUser existing = manager.lookupOrgUserByUsername( userName );
-
-        if ( existing == null ) {
-            logger.warn( "Could not determine an admin for colliding username '{}'.  Skipping", userName );
-            return;
-        }
-
-        users.remove( existing );
-
-        boolean collision = false;
-
-        for ( OrgUser other : users ) {
-
-            // same username and email, these will be merged later in the process,
-            // skip it
-            if ( other != null && other.getEmail() != null && other.getEmail().equals( existing.getEmail() ) ) {
-                logger.info(
-                        "Users with the same username '{}' have the same email '{}'. This will be resolved later in "
-                                + "the process, skipping", userName, existing.getEmail() );
-                continue;
-            }
-
-            // if we get here, the emails do not match, but the usernames do. Force
-            // both usernames to emails
-            collision = true;
-
-            setUserName(other, other.getEmail() );
-        }
-
-        if ( collision ) {
-            setUserName(existing, existing.getEmail() );
-        }
-    }
-
-
-    /** Set the username to the one provided, if we can't due to duplicate property issues, we fall back to user+uuid */
-    private void setUserName( OrgUser other, String newUserName ) throws Exception {
-
-        if ( dryRun ) {
-            logger.info("Would rename user {}:{} to {}", new Object[] { 
-                    other.getUsername(), other.getId(), newUserName });
-        } else {
-            logger.info( "Setting username to {} for user with username {} and id {}", new Object[] {
-                    newUserName, other.getUsername(), other.getId() } );
-            
-            manager.setOrgUserName( other, newUserName );
-        }
-    }
-
-
-    /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */
-    private void mergeAdmins( OrgUser sourceUser, OrgUser targetUser ) throws Exception {
-
-        Set<Org> sourceOrgs = manager.getUsersOrgs( sourceUser ); 
-
-        for ( Org org : sourceOrgs ) {
-
-            if ( dryRun ) {
-                logger.info("Would add org {}:{} to user {}:{}", new Object[] {
-                        org.getName(), org.getId(), targetUser.getUsername(), targetUser.getId(), });
-
-            } else {
-                logger.info( "Adding organization {}:{} to admin with email {} and id {}",
-                    new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
-
-                // copy it over to the target admin
-                manager.addUserToOrg( targetUser, org );
-            }
-        }
-
-        logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
-
-        if ( dryRun ) {
-            logger.info( "Would remove user {}:{}", new Object[]{
-                    sourceUser.getUsername(), sourceUser.getId() } );
-            
-        } else {
-            manager.removeOrgUser( sourceUser );
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
new file mode 100644
index 0000000..e5e9d43
--- /dev/null
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminRepairTest.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.usergrid.ServiceITSetup;
+import org.apache.usergrid.ServiceITSetupImpl;
+import org.apache.usergrid.ServiceITSuite;
+import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.apache.usergrid.management.UserInfo;
+import org.junit.ClassRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertNotNull;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Test duplicate admin repair.
+ */
+public class DuplicateAdminRepairTest {
+    
+    static final Logger logger = LoggerFactory.getLogger( DuplicateAdminRepairTest.class );
+    
+    @ClassRule
+    public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource );
+
+    
+    /**
+     * Test tool logic with mock manager that returns duplicates
+     */
+    @org.junit.Test
+    public void testMockWithDups() throws Exception {
+
+        DuplicateAdminRepair dor = new DuplicateAdminRepair();
+        MockManager mockManager = new MockManager();
+        dor.manager = mockManager;
+        dor.testing = true;
+
+        // the MockManager creates 2 pairs of duplicate orgs
+        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+
+        // verify that correct users indexed
+        assertUsersIndexed( mockManager );
+        
+        dor.startTool( new String[] {}, false ); // false means do not call System.exit()
+
+        // verify that correct users indexed
+        assertUsersIndexed( mockManager );
+
+        // verify that duplicate users are gone (the "a" users were the first ones created)
+        assertNull( "must remove user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNull( "must remove user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+
+        // and keepers survived
+        assertNotNull( "user1_b survived", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_b survived", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+    }
+
+    
+    private void assertUsersIndexed(MockManager mockManager ) {
+        assertEquals("user1_b is in the index",
+                mockManager.lookupOrgUserByUsername(mockManager.user1_b.getUsername()).getId(),
+                mockManager.user1_b.getId() );
+
+        assertEquals("user1_b is in the index",
+                mockManager.lookupOrgUserByEmail(mockManager.user1_b.getEmail()).getId(),
+                mockManager.user1_b.getId() );
+
+        assertEquals("user2_b is in the index",
+                mockManager.lookupOrgUserByUsername(mockManager.user2_b.getUsername()).getId(),
+                mockManager.user2_b.getId() );
+
+        assertEquals("user2_b is in the index",
+                mockManager.lookupOrgUserByEmail(mockManager.user2_b.getEmail()).getId(),
+                mockManager.user2_b.getId() ); 
+    }
+    
+
+    @org.junit.Test
+    public void testDryRun() throws Exception {
+
+        DuplicateAdminRepair dor = new DuplicateAdminRepair();
+        MockManager mockManager = new MockManager();
+        dor.manager = mockManager;
+        dor.testing = true;
+
+        // the MockManager creates 2 pairs of duplicate orgs
+        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+
+        // verify that correct users indexed
+        assertUsersIndexed( mockManager );
+
+        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
+
+        // verify that correct users indexed
+        assertUsersIndexed( mockManager );
+        
+        // insure nothng was deleted by dry-run
+        assertNotNull( "dryrun should not delete user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "dryrun should not delete user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "dryrun should not delete user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "dryrun should not delete user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));  
+    }
+
+    
+    /**
+     * Smoke test: does "real" manager run without throwing exceptions?
+     */
+    @org.junit.Test
+    public void testManagerNoDups() throws Exception {
+
+        // create two orgs each with owning user
+
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random2, "user_" + random2, "user_" + random2,
+                "user_" + random2 + "@example.com", "password" );
+
+        // Add user1 to org2
+
+        setup.getMgmtSvc().addAdminUserToOrganization(
+                orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false );
+
+        DuplicateAdminRepair dor = new DuplicateAdminRepair();
+
+        dor.startTool( new String[]{}, false );  // false means do not call System.exit()
+
+        assertTrue( true ); // we're happy if we get to this point
+    }
+
+    
+    @org.junit.Test
+    public void testManagerLookupMethods() throws Exception {
+        
+        // create two orgs each with owning user
+
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random2, "user_" + random2, "user_" + random2,
+                "user_" + random2 + "@example.com", "password" );
+
+        DuplicateAdminRepair dor = new DuplicateAdminRepair(setup.getEmf(), setup.getMgmtSvc());
+        dor.manager = dor.createNewRepairManager(); // test the real manager 
+
+        // start the tool so that Spring, Cassandra, etc/ gets initialized
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
+
+        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, true );
+        
+        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); 
+        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
+
+        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, false );
+    }
+    
+    
+    private void testManagerLookupMethods( DuplicateAdminRepair dor, 
+                                    OrganizationOwnerInfo info1, 
+                                    OrganizationOwnerInfo info2,
+                                    boolean usersExist ) throws Exception {
+        if ( usersExist ) {
+            
+            assertNotNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ));
+            assertNotNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ));
+
+            assertNotNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ));
+            assertNotNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ));
+
+            assertNotNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ));
+            assertNotNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() )); 
+            
+        } else {
+
+            assertNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ) );
+            assertNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ) );
+
+            assertNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ) );
+            assertNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ) );
+
+            assertNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ) );
+            assertNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() ) );
+        }
+    }
+
+
+    @org.junit.Test
+    public void testManagerOrgUserUpdateMethod() throws Exception {
+
+        // create an org with an admin user
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        DuplicateAdminRepair dor = new DuplicateAdminRepair(setup.getEmf(), setup.getMgmtSvc());
+        dor.manager = dor.createNewRepairManager(); // test the real manager 
+
+        // start the tool so that Spring, Cassandra, etc/ gets initialized
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
+
+        UserInfo userInfo = setup.getMgmtSvc().getAdminUserByUuid( orgOwnerInfo1.getOwner().getUuid() );
+        OrgUser user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( userInfo.getUsername(), user.getUsername());
+        assertEquals( userInfo.getEmail(), user.getEmail());
+
+        // change user's username using updateOrgUser()
+        String newUsername = "boom_" + random1;
+        user.setUsername(newUsername);
+        dor.manager.updateOrgUser( user );
+        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( newUsername, user.getUsername());
+        
+        // change user's username using setOrgUserName()
+        newUsername = "blammo_" + random1;
+        dor.manager.setOrgUserName( user, newUsername );
+        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( newUsername, user.getUsername());
+    }
+    
+
+    /**
+     * Extend mock manager to add a pair of duplicate users.
+     */
+    static class MockManager extends MockUserOrgManager {
+
+        OrgUser user1_a;
+        OrgUser user1_b;
+        OrgUser user2_a;
+        OrgUser user2_b;
+        
+        public MockManager() throws Exception {
+            
+            super(1); // ask parent to create one pair of duplicate orgs
+            
+            Org org = orgsById.values().iterator().next();
+          
+            String sfx = RandomStringUtils.randomAlphanumeric(10);
+
+            // user1 a and b have duplicate usernames AND DUPLICATE EMAIL ADDRESSES
+            
+            user1_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user1_a, org );
+            pause(100);
+
+            user1_b = createOrgUser( UUID.randomUUID(), "Username_"+sfx, "Username_"+sfx+"@example.com" );
+            addUserToOrg( user1_b, org );
+            pause(100);
+
+            // user2 a and b have duplicate usernames AND DIFFERENT EMAIL ADDRESSES 
+            
+            user2_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user2_a, org );
+            pause(100);
+
+            user2_b = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user2_b, org );
+            pause(100);
+
+        }
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
deleted file mode 100644
index 6676b87..0000000
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.usergrid.tools;
-
-
-import org.apache.commons.lang3.RandomStringUtils;
-import org.apache.usergrid.ServiceITSetup;
-import org.apache.usergrid.ServiceITSetupImpl;
-import org.apache.usergrid.ServiceITSuite;
-import org.apache.usergrid.management.OrganizationOwnerInfo;
-import org.apache.usergrid.management.UserInfo;
-import org.junit.ClassRule;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.UUID;
-
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.assertNotNull;
-import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
-import static org.junit.Assert.assertEquals;
-
-
-/**
- * Test duplicate admin repair.
- */
-public class DuplicateAdminUserRepairTest {
-    
-    static final Logger logger = LoggerFactory.getLogger( DuplicateAdminUserRepairTest.class );
-    
-    @ClassRule
-    public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource );
-
-    
-    /**
-     * Test tool logic with mock manager that returns duplicates
-     */
-    @org.junit.Test
-    public void testMockWithDups() throws Exception {
-
-        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
-        MockManager mockManager = new MockManager();
-        dor.manager = mockManager;
-        dor.testing = true;
-
-        // the MockManager creates 2 pairs of duplicate orgs
-        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
-        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
-        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
-        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
-
-        // verify that correct users indexed
-        assertUsersIndexed( mockManager );
-        
-        dor.startTool( new String[] {}, false ); // false means do not call System.exit()
-
-        // verify that correct users indexed
-        assertUsersIndexed( mockManager );
-
-        // verify that duplicate users are gone (the "a" users were the first ones created)
-        assertNull( "must remove user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
-        assertNull( "must remove user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
-
-        // and keepers survived
-        assertNotNull( "user1_b survived", mockManager.getOrgUser( mockManager.user1_b.getId() ));
-        assertNotNull( "user2_b survived", mockManager.getOrgUser( mockManager.user2_b.getId() ));
-    }
-
-    
-    private void assertUsersIndexed(MockManager mockManager ) {
-        assertEquals("user1_b is in the index",
-                mockManager.lookupOrgUserByUsername(mockManager.user1_b.getUsername()).getId(),
-                mockManager.user1_b.getId() );
-
-        assertEquals("user1_b is in the index",
-                mockManager.lookupOrgUserByEmail(mockManager.user1_b.getEmail()).getId(),
-                mockManager.user1_b.getId() );
-
-        assertEquals("user2_b is in the index",
-                mockManager.lookupOrgUserByUsername(mockManager.user2_b.getUsername()).getId(),
-                mockManager.user2_b.getId() );
-
-        assertEquals("user2_b is in the index",
-                mockManager.lookupOrgUserByEmail(mockManager.user2_b.getEmail()).getId(),
-                mockManager.user2_b.getId() ); 
-    }
-    
-
-    @org.junit.Test
-    public void testDryRun() throws Exception {
-
-        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
-        MockManager mockManager = new MockManager();
-        dor.manager = mockManager;
-        dor.testing = true;
-
-        // the MockManager creates 2 pairs of duplicate orgs
-        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
-        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
-        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
-        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
-
-        // verify that correct users indexed
-        assertUsersIndexed( mockManager );
-
-        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
-
-        // verify that correct users indexed
-        assertUsersIndexed( mockManager );
-        
-        // insure nothng was deleted by dry-run
-        assertNotNull( "dryrun should not delete user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
-        assertNotNull( "dryrun should not delete user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
-        assertNotNull( "dryrun should not delete user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
-        assertNotNull( "dryrun should not delete user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));  
-    }
-
-    
-    /**
-     * Smoke test: does "real" manager run without throwing exceptions?
-     */
-    @org.junit.Test
-    public void testManagerNoDups() throws Exception {
-
-        // create two orgs each with owning user
-
-        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
-        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
-                "org_" + random1, "user_" + random1, "user_" + random1,
-                "user_" + random1 + "@example.com", "password" );
-
-        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
-        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
-                "org_" + random2, "user_" + random2, "user_" + random2,
-                "user_" + random2 + "@example.com", "password" );
-
-        // Add user1 to org2
-
-        setup.getMgmtSvc().addAdminUserToOrganization(
-                orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false );
-
-        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
-
-        dor.startTool( new String[]{}, false );  // false means do not call System.exit()
-
-        assertTrue( true ); // we're happy if we get to this point
-    }
-
-    
-    @org.junit.Test
-    public void testManagerLookupMethods() throws Exception {
-        
-        // create two orgs each with owning user
-
-        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
-        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
-                "org_" + random1, "user_" + random1, "user_" + random1,
-                "user_" + random1 + "@example.com", "password" );
-
-        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
-        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
-                "org_" + random2, "user_" + random2, "user_" + random2,
-                "user_" + random2 + "@example.com", "password" );
-
-        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(setup.getEmf(), setup.getMgmtSvc());
-        dor.manager = dor.createNewRepairManager(); // test the real manager 
-
-        // start the tool so that Spring, Cassandra, etc/ gets initialized
-        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
-
-        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, true );
-        
-        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); 
-        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
-
-        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, false );
-    }
-    
-    
-    private void testManagerLookupMethods( DuplicateAdminUserRepair dor, 
-                                    OrganizationOwnerInfo info1, 
-                                    OrganizationOwnerInfo info2,
-                                    boolean usersExist ) throws Exception {
-        if ( usersExist ) {
-            
-            assertNotNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ));
-            assertNotNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ));
-
-            assertNotNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ));
-            assertNotNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ));
-
-            assertNotNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ));
-            assertNotNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() )); 
-            
-        } else {
-
-            assertNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ) );
-            assertNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ) );
-
-            assertNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ) );
-            assertNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ) );
-
-            assertNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ) );
-            assertNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() ) );
-        }
-    }
-
-
-    @org.junit.Test
-    public void testManagerOrgUserUpdateMethod() throws Exception {
-
-        // create an org with an admin user
-        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
-        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
-                "org_" + random1, "user_" + random1, "user_" + random1,
-                "user_" + random1 + "@example.com", "password" );
-
-        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(setup.getEmf(), setup.getMgmtSvc());
-        dor.manager = dor.createNewRepairManager(); // test the real manager 
-
-        // start the tool so that Spring, Cassandra, etc/ gets initialized
-        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
-
-        UserInfo userInfo = setup.getMgmtSvc().getAdminUserByUuid( orgOwnerInfo1.getOwner().getUuid() );
-        OrgUser user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
-        assertEquals( userInfo.getUsername(), user.getUsername());
-        assertEquals( userInfo.getEmail(), user.getEmail());
-
-        // change user's username using updateOrgUser()
-        String newUsername = "boom_" + random1;
-        user.setUsername(newUsername);
-        dor.manager.updateOrgUser( user );
-        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
-        assertEquals( newUsername, user.getUsername());
-        
-        // change user's username using setOrgUserName()
-        newUsername = "blammo_" + random1;
-        dor.manager.setOrgUserName( user, newUsername );
-        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
-        assertEquals( newUsername, user.getUsername());
-    }
-    
-
-    /**
-     * Extend mock manager to add a pair of duplicate users.
-     */
-    static class MockManager extends MockUserOrgManager {
-
-        OrgUser user1_a;
-        OrgUser user1_b;
-        OrgUser user2_a;
-        OrgUser user2_b;
-        
-        public MockManager() throws Exception {
-            
-            super(1); // ask parent to create one pair of duplicate orgs
-            
-            Org org = orgsById.values().iterator().next();
-          
-            String sfx = RandomStringUtils.randomAlphanumeric(10);
-
-            // user1 a and b have duplicate usernames AND DUPLICATE EMAIL ADDRESSES
-            
-            user1_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
-            addUserToOrg( user1_a, org );
-            pause(100);
-
-            user1_b = createOrgUser( UUID.randomUUID(), "Username_"+sfx, "Username_"+sfx+"@example.com" );
-            addUserToOrg( user1_b, org );
-            pause(100);
-
-            // user2 a and b have duplicate usernames AND DIFFERENT EMAIL ADDRESSES 
-            
-            user2_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
-            addUserToOrg( user2_a, org );
-            pause(100);
-
-            user2_b = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
-            addUserToOrg( user2_b, org );
-            pause(100);
-
-        }
-        
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/ceb81076/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
index 4b50f43..818c1b8 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
@@ -169,7 +169,7 @@ class MockUserOrgManager implements UserOrgInterface {
         for (String orgName : duplicatesByName.keySet()) {
             Set<Org> orgs = duplicatesByName.get( orgName );
             for (Org org : orgs) {
-                DuplicateAdminUserRepairTest.logger.info( "Duplicate org {}:{}", orgName, org.getId() );
+                DuplicateAdminRepairTest.logger.info( "Duplicate org {}:{}", orgName, org.getId() );
             }
         }
     }


[7/9] usergrid git commit: change selectBest() logic to favor org name that is in the index.

Posted by sn...@apache.org.
change selectBest() logic to favor org name that is in the index.


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/a1b66850
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/a1b66850
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/a1b66850

Branch: refs/heads/1.x
Commit: a1b6685080f8e23d8e31b2d3b46ea53916d0607a
Parents: 0316b57
Author: Dave Johnson <sn...@apache.org>
Authored: Wed Jan 13 09:31:06 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Wed Jan 13 09:31:06 2016 -0500

----------------------------------------------------------------------
 .../usergrid/tools/DuplicateAdminRepair.java    | 63 +++++++++++---------
 .../usergrid/tools/DuplicateOrgRepair.java      | 19 +-----
 .../apache/usergrid/tools/UserOrgInterface.java | 21 ++++++-
 .../apache/usergrid/tools/UserOrgManager.java   | 21 ++++++-
 .../usergrid/tools/MockUserOrgManager.java      | 15 +++++
 5 files changed, 92 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/a1b66850/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
index 9909918..79e403d 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
@@ -154,42 +154,41 @@ public class DuplicateAdminRepair extends ToolBase {
         }
 
         for ( String email : emails.keySet() ) {
+          
+            // go through a set of users with duplicate emails
+            Collection<OrgUser> usersWithDupEmails = emails.get( email );
             
-            Collection<OrgUser> users = emails.get( email );
-
-            if ( users.size() > 1 ) {
-                // get the admin the same way as the rest tier, this way the OTHER
-                // admins will be removed
-                OrgUser targetUser = manager.lookupOrgUserByEmail( email );
+            if ( usersWithDupEmails.size() > 1 ) {
+                
+                // get the user that is in the users-by-email index, it's the keeper
+                OrgUser indexedUser = manager.lookupOrgUserByEmail( email );
 
-                if ( targetUser == null ) {
+                if ( indexedUser == null ) {
 
-                    List<OrgUser> tempUsers = new ArrayList<OrgUser>( users );
+                    // no user is indexed with that email, pick oldest as keeper
+                    List<OrgUser> tempUsers = new ArrayList<OrgUser>( usersWithDupEmails );
                     Collections.sort( tempUsers );
+                    OrgUser oldestOfLot = tempUsers.get( 0 );
 
-                    OrgUser toLoad = tempUsers.get( 0 );
-
-                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad );
-                    targetUser = toLoad;
-
-                    users.remove( toLoad );
+                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, oldestOfLot );
+                    indexedUser = oldestOfLot;
                 }
 
-                users.remove( targetUser );
-
-                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, targetUser.getId() );
+                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, indexedUser.getId() );
 
-                for ( OrgUser orgUser : users ) {
-                    mergeAdmins( orgUser, targetUser );
+                for ( OrgUser orgUser : usersWithDupEmails ) {
+                    if ( !orgUser.getId().equals( indexedUser.getId() )) {
+                        mergeAdmins( orgUser, indexedUser );
+                    }
                 }
 
                 // force the index update after all other admins have been merged
                 if ( dryRun ) {
                     logger.info("Would force re-index of 'keeper' user {}:{}", 
-                            targetUser.getUsername(), targetUser.getId());
+                            indexedUser.getUsername(), indexedUser.getId());
                 } else {
-                    logger.info( "Forcing re-index of admin with email {} and id {}", email, targetUser.getId());
-                    manager.updateOrgUser( targetUser );
+                    logger.info( "Forcing re-index of admin with email {} and id {}", email, indexedUser.getId());
+                    manager.updateOrgUser( indexedUser );
                 }
             }
         }
@@ -257,30 +256,36 @@ public class DuplicateAdminRepair extends ToolBase {
 
     /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */
     private void mergeAdmins( OrgUser sourceUser, OrgUser targetUser ) throws Exception {
+        
+        logger.info("---> Merging user {}:{} into {}:{}", new Object[] { 
+               sourceUser.getEmail(), sourceUser.getId(), targetUser.getEmail(), targetUser.getId() 
+        } );
 
         Set<Org> sourceOrgs = manager.getUsersOrgs( sourceUser ); 
 
         for ( Org org : sourceOrgs ) {
 
             if ( dryRun ) {
-                logger.info("Would add org {}:{} to user {}:{}", new Object[] {
-                        org.getName(), org.getId(), targetUser.getUsername(), targetUser.getId(), });
+                logger.info( "Would add organization {}:{} to admin with email {} and id {}",
+                        new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
 
             } else {
                 logger.info( "Adding organization {}:{} to admin with email {} and id {}",
-                    new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
+                        new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
 
-                // copy it over to the target admin
+                // add targetUser to sourceUser's org
                 manager.addUserToOrg( targetUser, org );
             }
         }
 
         if ( dryRun ) {
-            logger.info( "Would remove user {}:{}", new Object[]{
-                    sourceUser.getUsername(), sourceUser.getId() } );
+            logger.info( "Would remove admin with email {} and id {} from system",
+                    sourceUser.getEmail(), sourceUser.getId() );
             
         } else {
-            logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
+            logger.info( "Removing admin with email {} and id {} from system",
+                    sourceUser.getEmail(), sourceUser.getId() );
+            
             manager.removeOrgUser( sourceUser );
         }
     }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/a1b66850/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
index f0f14c7..7b940fa 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
@@ -330,7 +330,7 @@ public class DuplicateOrgRepair extends ToolBase {
         
         for ( String dupName : duplicatesByName.keySet() ) {
             Set<Org> duplicateOrgs = duplicatesByName.get(dupName);
-            Org bestOrg = selectBest( duplicateOrgs );
+            Org bestOrg = manager.selectBest( duplicateOrgs );
             
             for ( Org org : duplicateOrgs ) {
                 
@@ -402,7 +402,7 @@ public class DuplicateOrgRepair extends ToolBase {
     private void removeDuplicateOrgs() throws Exception {
         for ( String dupName : duplicatesByName.keySet() ) {
             Set<Org> orgs = duplicatesByName.get( dupName );
-            Org best = selectBest( orgs );
+            Org best = manager.selectBest( orgs );
             for ( Org candidate : orgs ) {
                 if ( !candidate.equals(best) ) {
                     if ( dryRun ) {
@@ -414,21 +414,8 @@ public class DuplicateOrgRepair extends ToolBase {
                 }
             }
         }
+        
         logger.info( "DuplicateOrgRepair renamed/removed duplicate orgs"); 
     }
 
-
-    /**
-     * select best org from a set of duplicates by picking the oldest org
-     */
-    public Org selectBest(Set<Org> orgs) throws Exception {
-        Org oldest = null;
-        for ( Org org :orgs ) {
-            if ( oldest == null || org.compareTo( oldest ) < 0 ) {
-                oldest = org;
-            }
-        }
-        return oldest;
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/a1b66850/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
index b756f32..ab4b7e1 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
@@ -16,6 +16,7 @@
  */
 package org.apache.usergrid.tools;
 
+import org.apache.commons.lang3.builder.EqualsBuilder;
 import rx.Observable;
 
 import java.util.Map;
@@ -63,6 +64,8 @@ interface UserOrgInterface {
     void updateOrgUser(OrgUser targetUserEntity) throws Exception;
 
     void setOrgUserName(OrgUser other, String newUserName) throws Exception;
+    
+    Org selectBest( Set<Org> candidates ) throws Exception;
 
     class Org implements Comparable<Org> {
         private UUID id;
@@ -145,7 +148,23 @@ interface UserOrgInterface {
         public long getCreated() {
             return created;
         }
-        
+
+        @Override
+        public boolean equals( Object obj ) {
+            if (obj == null) { return false; }
+            if (obj == this) { return true; }
+            if (obj.getClass() != getClass()) {
+                return false;
+            }
+            OrgUser rhs = (OrgUser) obj;
+            return new EqualsBuilder().appendSuper(super.equals(obj))
+                    .append(id,       rhs.id)
+                    .append(username, rhs.username)
+                    .append(email,    rhs.email)
+                    .append(created,  rhs.created)
+                    .isEquals();
+        }
+
         @Override
         public int compareTo(OrgUser o) {
             return Long.compare( this.created, o.created );

http://git-wip-us.apache.org/repos/asf/usergrid/blob/a1b66850/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
index 383fb9b..7f82d35 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
@@ -343,7 +343,7 @@ class UserOrgManager implements UserOrgInterface {
         em.update( user );
     }
 
-    
+
     @Override
     public void setOrgUserName(OrgUser other, String newUserName ) throws Exception {
         
@@ -363,4 +363,23 @@ class UserOrgManager implements UserOrgInterface {
             setOrgUserName( other, String.format( "%s-%s", other.getUsername(), other.getId() ) );
         }
     }
+
+
+    /**
+     * Select best org from a set of duplicates by picking the one that is indexed, or the oldest.
+     */
+    @Override
+    public Org selectBest(Set<Org> orgs) throws Exception {
+        Org oldest = null;
+        for ( Org org :orgs ) {
+            OrganizationInfo info = managementService.getOrganizationByName( org.getName() );
+            if ( info != null ) {
+                return org; 
+            }
+            if ( oldest == null || org.compareTo( oldest ) < 0 ) {
+                oldest = org;
+            }
+        }
+        return oldest;
+    }
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/a1b66850/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
index 818c1b8..4e2fd95 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
@@ -221,6 +221,21 @@ class MockUserOrgManager implements UserOrgInterface {
         updateOrgUser( user ); // re-index user
     }
 
+    /**
+     * select best org from a set of duplicates by picking the oldest org
+     */
+    @Override
+    public Org selectBest(Set<Org> orgs) throws Exception {
+        Org oldest = null;
+        for ( Org org :orgs ) {
+            if ( oldest == null || org.compareTo( oldest ) < 0 ) {
+                oldest = org;
+            }
+        }
+        return oldest;
+    }
+    
+
     // implemented for testing only
     OrgUser createOrgUser(UUID id, String name, String email) {
         OrgUser user = new OrgUser( id, name, email );


[3/9] usergrid git commit: Merge branch '1.x' into dup-admin-repair

Posted by sn...@apache.org.
Merge branch '1.x' into dup-admin-repair

Conflicts:
	stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/bb6198f9
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/bb6198f9
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/bb6198f9

Branch: refs/heads/1.x
Commit: bb6198f9f6d396799f922eb18aa22fbfca562296
Parents: 7324b5a a5045b8
Author: Dave Johnson <sn...@apache.org>
Authored: Mon Jan 11 16:37:54 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Mon Jan 11 16:37:54 2016 -0500

----------------------------------------------------------------------
 .../usergrid/tools/DuplicateOrgRepair.java      | 30 +------
 .../org/apache/usergrid/tools/ExportAdmins.java | 82 ++++++++++----------
 .../org/apache/usergrid/tools/ImportAdmins.java | 10 ++-
 .../org/apache/usergrid/tools/ToolBase.java     |  2 +-
 4 files changed, 53 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/bb6198f9/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
----------------------------------------------------------------------
diff --cc stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
index f4d76fd,4363dbd..f0f14c7
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
@@@ -168,9 -165,9 +168,9 @@@ public class DuplicateOrgRepair extend
  
          if ( manager == null ) { // we use a special manager when mockTesting
              if (dryRun) {
-                 manager = new DryRunManager( emf, managementService );
 -                manager = new DryRunManager();
++                manager = new DryRunUserOrgManager( emf, managementService );
              } else {
 -                manager = new RepairManager();
 +                manager = new UserOrgManager( emf, managementService );
              }
          } 
  
@@@ -431,32 -428,289 +431,4 @@@
          return oldest;
      }
  
 -    
 -    class RepairManager implements DuplicateOrgInterface {
--
-     static class DryRunManager extends UserOrgManager {
 -        @Override
 -        public Observable<Org> getOrgs() throws Exception {
--
-         public DryRunManager(EntityManagerFactory emf, ManagementService managementService ) {
-             super(emf, managementService);
 -            return Observable.create( new Observable.OnSubscribe<Org>() {
 -
 -                @Override
 -                public void call(Subscriber<? super Org> subscriber) {
 -                    subscriber.onStart();
 -                    try {
 -                        int count = 0;
 -
 -                        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
 -                        String queryString = "select *";
 -                        Query query = Query.fromQL( queryString );
 -                        query.withLimit( MAX_ENTITY_FETCH );
 -                        
 -                        Results results = em.searchCollection( em.getApplicationRef(), "groups", query );
 -
 -                        while (results.size() > 0) {
 -                            for (Entity orgEntity : results.getEntities()) {
 -                                
 -                                Org org = new Org(
 -                                    orgEntity.getUuid(), 
 -                                    orgEntity.getProperty( "path" )+"", 
 -                                    orgEntity.getCreated() );
 -                                org.sourceValue = orgEntity;
 -                                
 -                                subscriber.onNext( org );
 -
 -                                if ( count++ % 1000 == 0 ) {
 -                                    logger.info("Emitted {} orgs", count );
 -                                }
 -
 -                                // logger.info( "org: {}, \"{}\", {}", new Object[]{
 -                                //     orgEntity.getProperty( "path" ),
 -                                //     orgEntity.getUuid(),
 -                                //     orgEntity.getCreated()} );
 -                            }
 -                            if (results.getCursor() == null) {
 -                                break;
 -                            }
 -                            query.setCursor( results.getCursor() );
 -                            results = em.searchCollection( em.getApplicationRef(), "groups", query );
 -                        }
 -
 -                    } catch (Exception e) {
 -                        subscriber.onError( e );
 -                    }
 -                    subscriber.onCompleted();
 -                }
 -            } );
 -        }
 -
 -        @Override
 -        public Observable<OrgUser> getUsers() throws Exception {
 -
 -            return Observable.create( new Observable.OnSubscribe<OrgUser>() {
 -
 -                @Override
 -                public void call(Subscriber<? super OrgUser> subscriber) {
 -                    subscriber.onStart();
 -                    try {
 -                        int count = 0;
 -                        
 -                        Query query = new Query();
 -                        query.setLimit( MAX_ENTITY_FETCH );
 -                        query.setResultsLevel( Results.Level.ALL_PROPERTIES );
 -                        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
 -                        Results results = em.searchCollection( em.getApplicationRef(), "users", query );
 -
 -                        while (results.size() > 0) {
 -                            for (Entity entity : results.getList()) {
 -
 -                                OrgUser orgUser = new OrgUser( 
 -                                    entity.getUuid(), 
 -                                    entity.getProperty( "username" ) + "" );
 -                                orgUser.sourceValue = entity;
 -
 -                                subscriber.onNext( orgUser );
 -
 -                                if ( count++ % 1000 == 0 ) {
 -                                    logger.info("Emitted {} users", count );
 -                                }
 -
 -                                // logger.info( "org: {}, \"{}\", {}", new Object[]{
 -                                //     entity.getProperty( "path" ),
 -                                //     entity.getUuid(),
 -                                //     entity.getCreated()} );
 -                            }
 -                            if (results.getCursor() == null) {
 -                                break;
 -                            }
 -                            query.setCursor( results.getCursor() );
 -                            results = em.searchCollection( em.getApplicationRef(), "users", query );
 -                        }
 -
 -                    } catch (Exception e) {
 -                        subscriber.onError( e );
 -                    }
 -                    subscriber.onCompleted();
 -                }
 -            } );
 -        }
 -
 -        @Override
 -        public Set<Org> getUsersOrgs(OrgUser user) throws Exception {
 -           
 -            Set<Org> ret = new HashSet<Org>();
 -            
 -            Map<String, Object> orgData = managementService.getAdminUserOrganizationData( user.getId() );
 -           
 -            Map<String, Object> orgs = (Map<String, Object>)orgData.get("organizations");
 -            for ( String orgName : orgs.keySet() ) {
 -
 -                Map<String, Object> orgMap = (Map<String, Object>)orgs.get( orgName );
 -                Group group = managementService.getOrganizationProps( 
 -                        UUID.fromString( orgMap.get( "uuid" ).toString() ) );
 -
 -                Org org = new Org(
 -                    group.getUuid(),
 -                    group.getPath(), 
 -                    group.getCreated()
 -                );
 -                ret.add(org);   
 -            }
 -            
 -            return ret;
 -        }
 -
 -        
 -        @Override
 -        public void removeOrg(Org keeper, Org duplicate) throws Exception {
 -            
 -            // rename org so that it is no longer a duplicate
 -            EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
 -            em.delete( new SimpleEntityRef( "group", duplicate.getId() ));
 -            logger.info("Deleted org {}:{}", new Object[] { duplicate.getName(), duplicate.getId() });
 -
 -            // fix the org name index
 -            OrganizationInfo orgInfoKeeper = managementService.getOrganizationByUuid( keeper.getId() );
 -            try {
 -                managementService.updateOrganizationUniqueIndex( orgInfoKeeper, duplicate.getId() );
 -                logger.info("Updated index for keeper {}:{} not dup {}", new Object[] {
 -                        orgInfoKeeper.getName(), orgInfoKeeper.getUuid(), duplicate.getId() });
 -                
 -            } catch ( Exception e ) {
 -                // if there are multiple duplicates this will fail for all but one of them. That's OK
 -                logger.warn("Error repairing unique value keeper {} duplicate {}", 
 -                        keeper.getId(), duplicate.getId());
 -            }
 -        }
 -        
 -
 -        @Override
 -        public Set<OrgUser> getOrgUsers(Org org) throws Exception {
 -            
 -            Set<OrgUser> ret = new HashSet<OrgUser>();
 -            
 -            List<UserInfo> userInfos = managementService.getAdminUsersForOrganization( org.getId() );
 -            
 -            for ( UserInfo userInfo : userInfos ) {
 -                OrgUser orgUser = new OrgUser( userInfo.getUuid(), userInfo.getUsername() );
 -                ret.add(orgUser);
 -            }
 -            
 -            return ret;
 -        }
 -
 -        
 -        @Override
 -        public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
 -            // forcefully remove admin user from org
 -            managementService.removeAdminUserFromOrganization( user.getId(), org.getId(), true );
 -            logger.info("Removed user {}:{} from org {}:{}", new Object[] {
 -                    user.getName(), user.getId(), org.getName(), org.getId() });
 -        }
 -
 -        
 -        @Override
 -        public void addUserToOrg(OrgUser user, Org org) throws Exception {
 -            UserInfo userInfo = managementService.getAdminUserByUsername( user.getName() );
 -            OrganizationInfo orgInfo = managementService.getOrganizationByUuid( org.getId() );
 -            managementService.addAdminUserToOrganization( userInfo, orgInfo, false );
 -            logger.info("Added user {}:{} to org {}:{}", new Object[] {
 -                    user.getName(), user.getId(), org.getName(), org.getId() });
 -        }
 -
 -        
 -        @Override
 -        public Set<UUID> getOrgApps(Org org) throws Exception {
 -            BiMap<UUID, String> apps = managementService.getApplicationsForOrganization( org.getId() );
 -            return apps.keySet();
 -        }
 -
 -        
 -        @Override
 -        public void removeAppFromOrg(UUID appId, Org org) throws Exception {
 -            managementService.removeOrganizationApplication( org.getId(), appId );
 -            logger.info("Removed app {} from org {}:{}", new Object[] {
 -                    appId, org.getName(), org.getId() });
 -        }
 -
 -        
 -        @Override
 -        public void addAppToOrg(UUID appId, Org org) throws Exception {
 -            managementService.addApplicationToOrganization( org.getId(), appId );
 -            logger.info("Added app {} to org {}:{}", new Object[] {
 -                    appId, org.getName(), org.getId() });
 -        }
 -
 -        
 -        @Override
 -        public void logDuplicates(Map<String, Set<Org>> duplicatesByName) {
 -
 -            for ( String orgName : duplicatesByName.keySet() ) {
 -                Set<Org> orgs = duplicatesByName.get(orgName);
 -                for ( Org org : orgs ) {
 -                    Entity orgEntity = (Entity)org.sourceValue;
 -
 -                    StringBuilder sb = new StringBuilder();
 -                    sb.append(orgEntity.toString()).append(", ");
 -                    
 -                    try {
 -                        BiMap<UUID, String> apps = 
 -                            managementService.getApplicationsForOrganization( orgEntity.getUuid() );
 -                        String sep = "";
 -                        for ( UUID uuid : apps.keySet() ) {
 -                            String appName = apps.get(uuid);
 -                            sb.append(appName).append(":").append(uuid).append(sep);
 -                            sep = ", ";
 -                        }
 -                        
 -                    } catch (Exception e) {
 -                        logger.error("Error getting applications for org {}:{}", org.getName(), org.getId() );
 -                    }
 -                    
 -                    logger.info(sb.toString());
 -                }
 -            }
 -        }
 -
 -        
 -        @Override
 -        public Org getOrg(UUID uuid) throws Exception {
 -
 -            EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
 -            Entity entity = em.get( uuid );
 -
 -            Org org = new Org(
 -                    entity.getUuid(),
 -                    entity.getProperty( "path" )+"",
 -                    entity.getCreated() );
 -            org.sourceValue = entity;
 -            
 -            return org;
--        }
 -    }
 -
 -    
 -    class DryRunManager extends RepairManager {
--        
--        @Override
--        public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
--        }
--
--        @Override
--        public void addUserToOrg(OrgUser user, Org org) throws Exception {
--        }
--
--        @Override
--        public void addAppToOrg(UUID appId, Org org) throws Exception {
--        }
--
--        @Override
--        public void removeAppFromOrg(UUID appId, Org org) throws Exception {
--        }
--
--        @Override
--        public void removeOrg(Org keeper, Org duplicate) throws Exception {
--        }
--    }
--    
  }


[9/9] usergrid git commit: This closes #461

Posted by sn...@apache.org.
This closes #461


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/14d6cdf9
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/14d6cdf9
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/14d6cdf9

Branch: refs/heads/1.x
Commit: 14d6cdf97249cd798efc17d14f5bb5768b0b7cc2
Parents: e0be7dd a3a8b89
Author: Dave Johnson <sn...@apache.org>
Authored: Fri Feb 5 14:42:03 2016 -0800
Committer: Dave Johnson <sn...@apache.org>
Committed: Fri Feb 5 14:42:03 2016 -0800

----------------------------------------------------------------------
 .../usergrid/tools/DryRunUserOrgManager.java    |  62 +++
 .../apache/usergrid/tools/DupAdminRepair.java   | 311 ---------------
 .../usergrid/tools/DuplicateAdminRepair.java    | 293 ++++++++++++++
 .../usergrid/tools/DuplicateOrgInterface.java   | 112 ------
 .../usergrid/tools/DuplicateOrgRepair.java      | 359 ++---------------
 .../apache/usergrid/tools/UserOrgInterface.java | 173 +++++++++
 .../apache/usergrid/tools/UserOrgManager.java   | 385 +++++++++++++++++++
 .../tools/DuplicateAdminRepairTest.java         | 303 +++++++++++++++
 .../usergrid/tools/DuplicateOrgRepairTest.java  | 320 ++++-----------
 .../usergrid/tools/ExportImportAdminsTest.java  |   1 +
 .../usergrid/tools/MockUserOrgManager.java      | 248 ++++++++++++
 stack/tools/src/test/resources/log4j.properties |  47 +++
 12 files changed, 1620 insertions(+), 994 deletions(-)
----------------------------------------------------------------------



[4/9] usergrid git commit: Additional tests

Posted by sn...@apache.org.
Additional tests


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/e525f4fa
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/e525f4fa
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/e525f4fa

Branch: refs/heads/1.x
Commit: e525f4fa966b6f30911536cd20359514d9079701
Parents: bb6198f
Author: Dave Johnson <sn...@apache.org>
Authored: Mon Jan 11 18:04:17 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Mon Jan 11 18:04:17 2016 -0500

----------------------------------------------------------------------
 .../apache/usergrid/tools/UserOrgManager.java   | 42 ++++-----
 .../tools/DuplicateAdminUserRepairTest.java     | 95 ++++++++++++++++----
 .../usergrid/tools/MockUserOrgManager.java      | 26 +++++-
 3 files changed, 124 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/e525f4fa/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
index b18b8ca..383fb9b 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
@@ -282,32 +282,34 @@ class UserOrgManager implements UserOrgInterface {
 
     @Override
     public Org getOrg(UUID uuid) throws Exception {
-
         EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-        Entity entity = em.get( uuid );
-
-        Org org = new Org(
-                entity.getUuid(),
-                entity.getProperty( "path" ) + "",
-                entity.getCreated() );
-        org.sourceValue = entity;
-
-        return org;
+        Group entity = em.get( uuid , Group.class );
+        if ( entity != null ) {
+            Org org = new Org(
+                    entity.getUuid(),
+                    entity.getPath(),
+                    entity.getCreated() );
+            org.sourceValue = entity;
+            return org;
+        }
+        return null;
     }
 
+    
     @Override
     public OrgUser getOrgUser(UUID uuid) throws Exception {
         EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-        Entity entity = em.get( uuid );
-        
-        OrgUser user = new OrgUser( 
-            entity.getUuid(),
-            entity.getType(),
-            entity.getProperty("email")+"",
-            entity.getCreated()
-        );
-       
-        return user;
+        User entity = em.get( uuid, User.class );
+        if ( entity != null ) {
+            OrgUser user = new OrgUser(
+                    entity.getUuid(),
+                    entity.getUsername(),
+                    entity.getEmail(),
+                    entity.getCreated()
+            );
+            return user;
+        }
+        return null;
     }
 
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e525f4fa/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
index 1ba3766..6676b87 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
@@ -22,6 +22,7 @@ import org.apache.usergrid.ServiceITSetup;
 import org.apache.usergrid.ServiceITSetupImpl;
 import org.apache.usergrid.ServiceITSuite;
 import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.apache.usergrid.management.UserInfo;
 import org.junit.ClassRule;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,11 +32,12 @@ import java.util.UUID;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertNotNull;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
 import static org.junit.Assert.assertEquals;
 
 
 /**
- * Test duplicate org repair.
+ * Test duplicate admin repair.
  */
 public class DuplicateAdminUserRepairTest {
     
@@ -63,12 +65,12 @@ public class DuplicateAdminUserRepairTest {
         assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
 
         // verify that correct users indexed
-        usersIndexed( mockManager );
+        assertUsersIndexed( mockManager );
         
         dor.startTool( new String[] {}, false ); // false means do not call System.exit()
 
         // verify that correct users indexed
-        usersIndexed( mockManager );
+        assertUsersIndexed( mockManager );
 
         // verify that duplicate users are gone (the "a" users were the first ones created)
         assertNull( "must remove user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
@@ -80,7 +82,7 @@ public class DuplicateAdminUserRepairTest {
     }
 
     
-    private void usersIndexed( MockManager mockManager ) {
+    private void assertUsersIndexed(MockManager mockManager ) {
         assertEquals("user1_b is in the index",
                 mockManager.lookupOrgUserByUsername(mockManager.user1_b.getUsername()).getId(),
                 mockManager.user1_b.getId() );
@@ -114,12 +116,12 @@ public class DuplicateAdminUserRepairTest {
         assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
 
         // verify that correct users indexed
-        usersIndexed( mockManager );
+        assertUsersIndexed( mockManager );
 
         dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
 
         // verify that correct users indexed
-        usersIndexed( mockManager );
+        assertUsersIndexed( mockManager );
         
         // insure nothng was deleted by dry-run
         assertNotNull( "dryrun should not delete user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
@@ -156,14 +158,12 @@ public class DuplicateAdminUserRepairTest {
 
         dor.startTool( new String[]{}, false );  // false means do not call System.exit()
 
-        dor.startTool( new String[]{"dryrun", "true"}, false ); // false means do not call System.exit()
-
         assertTrue( true ); // we're happy if we get to this point
     }
 
     
     @org.junit.Test
-    public void testManagerMethods() throws Exception {
+    public void testManagerLookupMethods() throws Exception {
         
         // create two orgs each with owning user
 
@@ -183,17 +183,82 @@ public class DuplicateAdminUserRepairTest {
         // start the tool so that Spring, Cassandra, etc/ gets initialized
         dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
 
-        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() ));
-        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
-
+        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, true );
+        
         dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); 
         dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
-        
-        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() ));
-        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
+
+        testManagerLookupMethods( dor, orgOwnerInfo1, orgOwnerInfo2, false );
     }
     
     
+    private void testManagerLookupMethods( DuplicateAdminUserRepair dor, 
+                                    OrganizationOwnerInfo info1, 
+                                    OrganizationOwnerInfo info2,
+                                    boolean usersExist ) throws Exception {
+        if ( usersExist ) {
+            
+            assertNotNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ));
+            assertNotNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ));
+
+            assertNotNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ));
+            assertNotNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ));
+
+            assertNotNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ));
+            assertNotNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() )); 
+            
+        } else {
+
+            assertNull( dor.manager.getOrgUser( info1.getOwner().getUuid() ) );
+            assertNull( dor.manager.getOrgUser( info2.getOwner().getUuid() ) );
+
+            assertNull( dor.manager.lookupOrgUserByEmail( info1.getOwner().getEmail() ) );
+            assertNull( dor.manager.lookupOrgUserByEmail( info2.getOwner().getEmail() ) );
+
+            assertNull( dor.manager.lookupOrgUserByUsername( info1.getOwner().getUsername() ) );
+            assertNull( dor.manager.lookupOrgUserByUsername( info2.getOwner().getUsername() ) );
+        }
+    }
+
+
+    @org.junit.Test
+    public void testManagerOrgUserUpdateMethod() throws Exception {
+
+        // create an org with an admin user
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(setup.getEmf(), setup.getMgmtSvc());
+        dor.manager = dor.createNewRepairManager(); // test the real manager 
+
+        // start the tool so that Spring, Cassandra, etc/ gets initialized
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
+
+        UserInfo userInfo = setup.getMgmtSvc().getAdminUserByUuid( orgOwnerInfo1.getOwner().getUuid() );
+        OrgUser user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( userInfo.getUsername(), user.getUsername());
+        assertEquals( userInfo.getEmail(), user.getEmail());
+
+        // change user's username using updateOrgUser()
+        String newUsername = "boom_" + random1;
+        user.setUsername(newUsername);
+        dor.manager.updateOrgUser( user );
+        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( newUsername, user.getUsername());
+        
+        // change user's username using setOrgUserName()
+        newUsername = "blammo_" + random1;
+        dor.manager.setOrgUserName( user, newUsername );
+        user = dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() );
+        assertEquals( newUsername, user.getUsername());
+    }
+    
+
+    /**
+     * Extend mock manager to add a pair of duplicate users.
+     */
     static class MockManager extends MockUserOrgManager {
 
         OrgUser user1_a;

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e525f4fa/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
index e8323e4..4b50f43 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/MockUserOrgManager.java
@@ -1,12 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package org.apache.usergrid.tools;
 
 import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
 import com.google.common.collect.SetMultimap;
-import rx.*;
 
-import java.util.*;
-import java.util.Observable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
 
 /**
  * Mock manager implementation for mockTesting.


[2/9] usergrid git commit: Make duplicate admin repair tool testable, add dry-run feature and RxJava. Also, make DupOrgRepair case-insensitive

Posted by sn...@apache.org.
Make duplicate admin repair tool testable, add dry-run feature and RxJava. Also, make DupOrgRepair case-insensitive


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/7324b5a8
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/7324b5a8
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/7324b5a8

Branch: refs/heads/1.x
Commit: 7324b5a8312ce4e78abc8f4c01bcd1d46786e532
Parents: ebfa31a
Author: Dave Johnson <sn...@apache.org>
Authored: Mon Jan 11 16:33:28 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Mon Jan 11 16:33:28 2016 -0500

----------------------------------------------------------------------
 .../usergrid/tools/DryRunUserOrgManager.java    |  62 ++++
 .../tools/DuplicateAdminUserRepair.java         | 289 +++++++++++++++
 .../usergrid/tools/DuplicateOrgInterface.java   | 112 ------
 .../usergrid/tools/DuplicateOrgRepair.java      | 319 ++--------------
 .../apache/usergrid/tools/UserOrgInterface.java | 154 ++++++++
 .../apache/usergrid/tools/UserOrgManager.java   | 364 +++++++++++++++++++
 .../tools/DuplicateAdminUserRepairTest.java     | 236 ++++++++++++
 .../usergrid/tools/DuplicateOrgRepairTest.java  | 320 ++++------------
 .../usergrid/tools/ExportImportAdminsTest.java  |   1 +
 .../usergrid/tools/MockUserOrgManager.java      | 215 +++++++++++
 stack/tools/src/test/resources/log4j.properties |  47 +++
 11 files changed, 1476 insertions(+), 643 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java
new file mode 100644
index 0000000..966b18c
--- /dev/null
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DryRunUserOrgManager.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+import org.apache.usergrid.management.ManagementService;
+import org.apache.usergrid.persistence.EntityManagerFactory;
+
+import java.util.UUID;
+
+class DryRunUserOrgManager extends UserOrgManager {
+
+    public DryRunUserOrgManager(EntityManagerFactory emf, ManagementService managementService) {
+        super( emf, managementService );
+    }
+
+    @Override
+    public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
+    }
+
+    @Override
+    public void addUserToOrg(OrgUser user, Org org) throws Exception {
+    }
+
+    @Override
+    public void addAppToOrg(UUID appId, Org org) throws Exception {
+    }
+
+    @Override
+    public void removeOrgUser(OrgUser orgUser) {
+    }
+
+    @Override
+    public void updateOrgUser(OrgUser targetUserEntity) {
+    }
+
+    @Override
+    public void setOrgUserName(OrgUser other, String newUserName) {
+
+    }
+
+    @Override
+    public void removeAppFromOrg(UUID appId, Org org) throws Exception {
+    }
+
+    @Override
+    public void removeOrg(Org keeper, Org duplicate) throws Exception {
+    }
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
new file mode 100644
index 0000000..902eefa
--- /dev/null
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminUserRepair.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.usergrid.management.ManagementService;
+import org.apache.usergrid.persistence.EntityManagerFactory;
+import rx.functions.Action1;
+
+import java.util.*;
+
+import static org.apache.usergrid.tools.UserOrgInterface.Org;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
+
+
+/**
+ * Find duplicate admin users, delete the one that is not indexed.
+ */
+public class DuplicateAdminUserRepair extends ToolBase {
+
+    UserOrgInterface           manager = null;
+    
+    static final String        THREADS_ARG_NAME = "threads"; 
+    
+    int                        threadCount = 5;
+
+    static final String        DRYRUN_ARG_NAME = "dryrun";
+
+    boolean                    dryRun = false;
+
+    Multimap<String, OrgUser>  emails = HashMultimap.create();
+    
+    Multimap<String, OrgUser>  usernames = HashMultimap.create();
+
+    boolean                    testing = false;
+
+
+    DuplicateAdminUserRepair() {
+        super();
+    }
+
+    DuplicateAdminUserRepair(EntityManagerFactory emf, ManagementService managementService ) {
+        this();
+        this.emf = emf;
+        this.managementService = managementService;
+    }
+    
+    
+    @Override
+    @SuppressWarnings("static-access")
+    public Options createOptions() {
+
+        Options options = super.createOptions();
+
+        Option dryRunOption = OptionBuilder.hasArg()
+            .withType(Boolean.TRUE)
+            .withDescription( "-" + DRYRUN_ARG_NAME + " true to print what tool would do and do not alter data.")
+            .create( DRYRUN_ARG_NAME );
+        options.addOption( dryRunOption );
+        
+        Option writeThreadsOption = OptionBuilder.hasArg()
+            .withType(0)
+            .withDescription( "Write Threads -" + THREADS_ARG_NAME )
+            .create(THREADS_ARG_NAME);
+        options.addOption( writeThreadsOption );        
+        
+        return options;
+    }
+
+    
+    public UserOrgManager createNewRepairManager() {
+        return new UserOrgManager( emf, managementService );
+    }
+   
+    
+    @Override
+    public void runTool(CommandLine line) throws Exception {
+
+        startSpring();
+        setVerbose( line );
+
+        if (StringUtils.isNotEmpty( line.getOptionValue( THREADS_ARG_NAME ) )) {
+            try {
+                threadCount = Integer.parseInt( line.getOptionValue( THREADS_ARG_NAME ) );
+            } catch (NumberFormatException nfe) {
+                logger.error( "-" + THREADS_ARG_NAME + " must be specified as an integer. Aborting..." );
+                return;
+            }
+        }
+
+        if ( StringUtils.isNotEmpty( line.getOptionValue( DRYRUN_ARG_NAME ) )) {
+            dryRun = Boolean.parseBoolean( line.getOptionValue( DRYRUN_ARG_NAME ));
+        }
+
+        if ( manager == null ) { // we use a special manager when mockTesting
+            if (dryRun) {
+                manager = new DryRunUserOrgManager( emf, managementService );
+            } else {
+                manager = new UserOrgManager( emf, managementService );
+            }
+        } 
+
+        logger.info( "DuplicateAdminUserRepair tool starting up... manager: " + manager.getClass().getSimpleName() );
+       
+        // build multi-map of users by email and users by name
+        
+        manager.getUsers().doOnNext( new Action1<OrgUser>() {
+            @Override
+            public void call( OrgUser user ) {
+
+                if (user.getUsername() == null) {
+                    logger.warn( "User {} has no username", user.getId() );
+                    return;
+                }
+                if (user.getEmail() == null) {
+                    logger.warn( "User {} has no email", user.getId() );
+                    return;
+                }
+                emails.put( user.getEmail(), user );
+                usernames.put( user.getEmail(), user );
+                
+            }
+        } ).toBlocking().lastOrDefault( null );
+
+        for ( String username : usernames.keySet() ) {
+            Collection<OrgUser> users = usernames.get( username );
+
+            if ( users.size() > 1 ) {
+                logger.info( "Found multiple users with the username {}", username );
+
+                // force the username to be reset to the user's email
+                resolveUsernameConflicts( username, users );
+            }
+        }
+
+        for ( String email : emails.keySet() ) {
+            
+            Collection<OrgUser> users = emails.get( email );
+
+            if ( users.size() > 1 ) {
+                // get the admin the same way as the rest tier, this way the OTHER
+                // admins will be removed
+                OrgUser targetUser = manager.lookupOrgUserByEmail( email );
+
+                if ( targetUser == null ) {
+
+                    List<OrgUser> tempUsers = new ArrayList<OrgUser>( users );
+                    Collections.sort( tempUsers );
+
+                    OrgUser toLoad = tempUsers.get( 0 );
+
+                    logger.warn( "Could not load target user by email {}, loading by UUID {} instead", email, toLoad );
+                    targetUser = toLoad;
+
+                    users.remove( toLoad );
+                }
+
+                users.remove( targetUser );
+
+                logger.warn( "Found multiple admins with the email {}.  Retaining uuid {}", email, targetUser.getId() );
+
+                for ( OrgUser orgUser : users ) {
+                    mergeAdmins( orgUser, targetUser );
+                }
+
+                // force the index update after all other admins have been merged
+                if ( dryRun ) {
+                    logger.info("Would force re-index of 'keeper' user {}:{}", 
+                            targetUser.getUsername(), targetUser.getId());
+                } else {
+                    logger.info( "Forcing re-index of admin with email {} and id {}", email, targetUser.getId());
+                    manager.updateOrgUser( targetUser );
+                }
+            }
+        }
+
+        logger.info( "Repair complete" );
+    }
+
+
+    /**
+     * When our usernames are equal, we need to check if our emails are equal. If they're not, we need to change the one
+     * that DOES NOT get returned on a lookup by username
+     */
+    private void resolveUsernameConflicts( String userName, Collection<OrgUser> users ) throws Exception {
+        
+        // lookup the admin id
+        OrgUser existing = manager.lookupOrgUserByUsername( userName );
+
+        if ( existing == null ) {
+            logger.warn( "Could not determine an admin for colliding username '{}'.  Skipping", userName );
+            return;
+        }
+
+        users.remove( existing );
+
+        boolean collision = false;
+
+        for ( OrgUser other : users ) {
+
+            // same username and email, these will be merged later in the process,
+            // skip it
+            if ( other != null && other.getEmail() != null && other.getEmail().equals( existing.getEmail() ) ) {
+                logger.info(
+                        "Users with the same username '{}' have the same email '{}'. This will be resolved later in "
+                                + "the process, skipping", userName, existing.getEmail() );
+                continue;
+            }
+
+            // if we get here, the emails do not match, but the usernames do. Force
+            // both usernames to emails
+            collision = true;
+
+            setUserName(other, other.getEmail() );
+        }
+
+        if ( collision ) {
+            setUserName(existing, existing.getEmail() );
+        }
+    }
+
+
+    /** Set the username to the one provided, if we can't due to duplicate property issues, we fall back to user+uuid */
+    private void setUserName( OrgUser other, String newUserName ) throws Exception {
+
+        if ( dryRun ) {
+            logger.info("Would rename user {}:{} to {}", new Object[] { 
+                    other.getUsername(), other.getId(), newUserName });
+        } else {
+            logger.info( "Setting username to {} for user with username {} and id {}", new Object[] {
+                    newUserName, other.getUsername(), other.getId() } );
+            
+            manager.setOrgUserName( other, newUserName );
+        }
+    }
+
+
+    /** Merge the source admin to the target admin by copying oranizations. Then deletes the source admin */
+    private void mergeAdmins( OrgUser sourceUser, OrgUser targetUser ) throws Exception {
+
+        Set<Org> sourceOrgs = manager.getUsersOrgs( sourceUser ); 
+
+        for ( Org org : sourceOrgs ) {
+
+            if ( dryRun ) {
+                logger.info("Would add org {}:{} to user {}:{}", new Object[] {
+                        org.getName(), org.getId(), targetUser.getUsername(), targetUser.getId(), });
+
+            } else {
+                logger.info( "Adding organization {}:{} to admin with email {} and id {}",
+                    new Object[] { org.getName(), org.getId(), targetUser.getEmail(), targetUser.getId() } );
+
+                // copy it over to the target admin
+                manager.addUserToOrg( targetUser, org );
+            }
+        }
+
+        logger.info( "Deleting admin with email {} and id {}", sourceUser.getEmail(), sourceUser.getId() );
+
+        if ( dryRun ) {
+            logger.info( "Would remove user {}:{}", new Object[]{
+                    sourceUser.getUsername(), sourceUser.getId() } );
+            
+        } else {
+            manager.removeOrgUser( sourceUser );
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java
deleted file mode 100644
index ab708c9..0000000
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgInterface.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.usergrid.tools;
-
-import rx.Observable;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-
-/**
- * Abstraction to make duplicate org repair testable.
- */
-interface DuplicateOrgInterface {
-    
-    Observable<Org> getOrgs() throws Exception;
-    
-    Observable<OrgUser> getUsers() throws Exception;
-
-    Set<Org> getUsersOrgs(OrgUser user) throws Exception;
-
-    Set<OrgUser> getOrgUsers(Org org ) throws Exception;
-    
-    void removeOrg(Org keeper, Org duplicate) throws Exception;
-
-    void removeUserFromOrg( OrgUser user, Org org ) throws Exception;
-    
-    void addUserToOrg( OrgUser user, Org org ) throws Exception;
-
-    Set<UUID> getOrgApps(Org org) throws Exception;
-    
-    void removeAppFromOrg( UUID appId, Org org ) throws Exception;
-    
-    void addAppToOrg( UUID appId, Org org ) throws Exception;
-  
-    void logDuplicates(Map<String, Set<Org>> duplicatesByName);
-
-    Org getOrg(UUID keeperUuid) throws Exception;
-
-    class Org implements Comparable<Org> {
-        private UUID id;
-        private String name;
-        private long created;
-        public Object sourceValue;
-        
-        public Org( UUID id, String name, long created ) {
-            this.id = id;
-            this.name = name;
-            this.created = created;
-        }
-        
-        @Override
-        public boolean equals( Object o ) {
-            if ( o instanceof Org ) {
-                Org other = (Org)o;
-                return getId().equals( other.getId() );
-            }
-            return false;
-        }
-
-        @Override
-        public int compareTo(Org o) {
-            return Long.compare( this.created, o.created );
-        }
-
-        public UUID getId() {
-            return id;
-        }
-
-        public String getName() {
-            return name;
-        }
-
-        public long getCreated() {
-            return created;
-        }
-    }
-    
-    class OrgUser {
-        private UUID id;
-        private String name;
-        public Object sourceValue;
-        
-        public OrgUser( UUID id, String name ) {
-            this.id = id;
-            this.name = name;
-        }
-
-        public UUID getId() {
-            return id;
-        }
-
-        public String getName() {
-            return name;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
index a33ca0e..f4d76fd 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateOrgRepair.java
@@ -16,21 +16,14 @@
  */
 package org.apache.usergrid.tools;
 
-import com.google.common.collect.BiMap;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.OptionBuilder;
 import org.apache.commons.cli.Options;
-import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.usergrid.management.OrganizationInfo;
-import org.apache.usergrid.management.UserInfo;
+import org.apache.usergrid.management.ManagementService;
 import org.apache.usergrid.persistence.*;
-import org.apache.usergrid.persistence.cassandra.CassandraService;
-import org.apache.usergrid.persistence.entities.Group;
-import rx.Observable;
 import rx.Scheduler;
-import rx.Subscriber;
 import rx.functions.Action1;
 import rx.schedulers.Schedulers;
 
@@ -38,15 +31,15 @@ import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import static org.apache.usergrid.tools.DuplicateOrgInterface.Org;
-import static org.apache.usergrid.tools.DuplicateOrgInterface.OrgUser;
+import static org.apache.usergrid.tools.UserOrgInterface.Org;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
 
 /**
  * Find duplicate orgs, delete all but oldest of each and assign users to it.
  */
 public class DuplicateOrgRepair extends ToolBase {
 
-    DuplicateOrgInterface   manager = null;
+    UserOrgInterface        manager = null;
     
     Map<String, Set<Org>>   orgsByName = new HashMap<String, Set<Org>>();
     
@@ -74,6 +67,16 @@ public class DuplicateOrgRepair extends ToolBase {
 
     boolean                 testing = false;
 
+
+    DuplicateOrgRepair() {
+        super();
+    }
+    
+    DuplicateOrgRepair( EntityManagerFactory emf, ManagementService managementService ) {
+        this();
+        this.emf = emf;
+        this.managementService = managementService;
+    }
     
     @Override
     @SuppressWarnings("static-access")
@@ -165,9 +168,9 @@ public class DuplicateOrgRepair extends ToolBase {
 
         if ( manager == null ) { // we use a special manager when mockTesting
             if (dryRun) {
-                manager = new DryRunManager();
+                manager = new DryRunManager( emf, managementService );
             } else {
-                manager = new RepairManager();
+                manager = new UserOrgManager( emf, managementService );
             }
         } 
 
@@ -178,7 +181,7 @@ public class DuplicateOrgRepair extends ToolBase {
             Org org1 = manager.getOrg( org1uuid );
             Org org2 = manager.getOrg( org2uuid );
             
-            if ( org1.getName().equals( org2.getName() )) {
+            if ( org1.getName().equalsIgnoreCase( org2.getName() )) {
                 buildOrgMaps( org1, org2 );
             } else {
                 logger.error("org1 and org2 do not have same duplicate name");
@@ -203,8 +206,8 @@ public class DuplicateOrgRepair extends ToolBase {
     }
     
     
-    public RepairManager createNewRepairManager() {
-        return new RepairManager();
+    public UserOrgManager createNewRepairManager() {
+        return new UserOrgManager( emf, managementService );
     }
 
 
@@ -213,8 +216,8 @@ public class DuplicateOrgRepair extends ToolBase {
         Set<Org> orgs = new HashSet<Org>();
         orgs.add( org1 );
         orgs.add( org2 );
-        orgsByName.put( org1.getName(), orgs );
-        duplicatesByName.put( org1.getName(), orgs );
+        orgsByName.put(       org1.getName().toLowerCase(), orgs );
+        duplicatesByName.put( org1.getName().toLowerCase(), orgs );
 
         orgsById.put( org1.getId(), org1 );
         orgsById.put( org2.getId(), org2 );
@@ -250,12 +253,12 @@ public class DuplicateOrgRepair extends ToolBase {
                
                 // orgs by name and duplicate orgs by name maps
                 
-                Set<Org> orgs = orgsByName.get( org.getName() );
+                Set<Org> orgs = orgsByName.get( org.getName().toLowerCase() );
                 if (orgs == null) {
                     orgs = new HashSet<Org>();
-                    orgsByName.put( org.getName(), orgs );
+                    orgsByName.put( org.getName().toLowerCase(), orgs );
                 } else {
-                    duplicatesByName.put( org.getName(), orgs );
+                    duplicatesByName.put( org.getName().toLowerCase(), orgs );
                 }
                 orgs.add( org );
                 
@@ -310,7 +313,7 @@ public class DuplicateOrgRepair extends ToolBase {
                     }
 
                 } catch (Exception e) {
-                    logger.error("Error getting orgs for user {}:{}", user.getName(), user.getId());
+                    logger.error("Error getting orgs for user {}:{}", user.getUsername(), user.getId());
                     logger.error("Stack trace is: ", e);
                 }
             }
@@ -338,24 +341,24 @@ public class DuplicateOrgRepair extends ToolBase {
                     for (OrgUser user : orgUsers) {
                         if (dryRun) {
                             Object[] args = new Object[]{
-                                    user.getName(), user.getId(), bestOrg.getName(), bestOrg.getId()};
+                                    user.getUsername(), user.getId(), bestOrg.getName(), bestOrg.getId()};
                             logger.info( "Would add user {}:{} to org {}:{}", args );
                             args = new Object[]{
-                                    user.getName(), user.getId(), org.getName(), org.getId()};
+                                    user.getUsername(), user.getId(), org.getName(), org.getId()};
                             logger.info( "Would remove user {}:{}  org {}:{}", args );
                         } else {
                             try {
                                 manager.addUserToOrg( user, bestOrg );
                             } catch ( Exception e ) {
                                 Object[] args = new Object[]{ 
-                                        user.getName(), user.getId(), bestOrg.getName(), bestOrg.getId()};
+                                        user.getUsername(), user.getId(), bestOrg.getName(), bestOrg.getId()};
                                 logger.error( "Error adding user {}:{} to org {}:{}", args );
                             }
                             try {
                                 manager.removeUserFromOrg( user, org );
                             } catch ( Exception e ) {
                                 Object[] args = new Object[]{ 
-                                        user.getName(), user.getId(), org.getName(), org.getId()};
+                                        user.getUsername(), user.getId(), org.getName(), org.getId()};
                                 logger.info( "Error removing user {}:{}  org {}:{}", args );
                             }
                         }
@@ -428,270 +431,12 @@ public class DuplicateOrgRepair extends ToolBase {
         return oldest;
     }
 
-    
-    class RepairManager implements DuplicateOrgInterface {
-
-        private boolean dryRun = true;
-
-        @Override
-        public Observable<Org> getOrgs() throws Exception {
-
-            return Observable.create( new Observable.OnSubscribe<Org>() {
-
-                @Override
-                public void call(Subscriber<? super Org> subscriber) {
-                    subscriber.onStart();
-                    try {
-                        int count = 0;
-                        
-                        Query query = new Query();
-                        query.setLimit( MAX_ENTITY_FETCH );
-                        query.setResultsLevel( Results.Level.ALL_PROPERTIES );
-                        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-                        Results results = em.searchCollection( em.getApplicationRef(), "groups", query );
-
-                        while (results.size() > 0) {
-                            for (Entity orgEntity : results.getList()) {
-                                
-                                Org org = new Org(
-                                    orgEntity.getUuid(), 
-                                    orgEntity.getProperty( "path" )+"", 
-                                    orgEntity.getCreated() );
-                                org.sourceValue = orgEntity;
-                                
-                                subscriber.onNext( org );
-
-                                if ( count++ % 1000 == 0 ) {
-                                    logger.info("Emitted {} orgs", count );
-                                }
-
-                                // logger.info( "org: {}, \"{}\", {}", new Object[]{
-                                //     orgEntity.getProperty( "path" ),
-                                //     orgEntity.getUuid(),
-                                //     orgEntity.getCreated()} );
-                            }
-                            if (results.getCursor() == null) {
-                                break;
-                            }
-                            query.setCursor( results.getCursor() );
-                            results = em.searchCollection( em.getApplicationRef(), "groups", query );
-                        }
-
-                    } catch (Exception e) {
-                        subscriber.onError( e );
-                    }
-                    subscriber.onCompleted();
-                }
-            } );
-        }
-
-        @Override
-        public Observable<OrgUser> getUsers() throws Exception {
-
-            return Observable.create( new Observable.OnSubscribe<OrgUser>() {
-
-                @Override
-                public void call(Subscriber<? super OrgUser> subscriber) {
-                    subscriber.onStart();
-                    try {
-                        int count = 0;
-                        
-                        Query query = new Query();
-                        query.setLimit( MAX_ENTITY_FETCH );
-                        query.setResultsLevel( Results.Level.ALL_PROPERTIES );
-                        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-                        Results results = em.searchCollection( em.getApplicationRef(), "users", query );
-
-                        while (results.size() > 0) {
-                            for (Entity entity : results.getList()) {
-
-                                OrgUser orgUser = new OrgUser( 
-                                    entity.getUuid(), 
-                                    entity.getProperty( "username" ) + "" );
-                                orgUser.sourceValue = entity;
-
-                                subscriber.onNext( orgUser );
-
-                                if ( count++ % 1000 == 0 ) {
-                                    logger.info("Emitted {} users", count );
-                                }
-
-                                // logger.info( "org: {}, \"{}\", {}", new Object[]{
-                                //     entity.getProperty( "path" ),
-                                //     entity.getUuid(),
-                                //     entity.getCreated()} );
-                            }
-                            if (results.getCursor() == null) {
-                                break;
-                            }
-                            query.setCursor( results.getCursor() );
-                            results = em.searchCollection( em.getApplicationRef(), "users", query );
-                        }
-
-                    } catch (Exception e) {
-                        subscriber.onError( e );
-                    }
-                    subscriber.onCompleted();
-                }
-            } );
-        }
-
-        @Override
-        public Set<Org> getUsersOrgs(OrgUser user) throws Exception {
-           
-            Set<Org> ret = new HashSet<Org>();
-            
-            Map<String, Object> orgData = managementService.getAdminUserOrganizationData( user.getId() );
-           
-            Map<String, Object> orgs = (Map<String, Object>)orgData.get("organizations");
-            for ( String orgName : orgs.keySet() ) {
-
-                Map<String, Object> orgMap = (Map<String, Object>)orgs.get( orgName );
-                Group group = managementService.getOrganizationProps( 
-                        UUID.fromString( orgMap.get( "uuid" ).toString() ) );
-
-                Org org = new Org(
-                    group.getUuid(),
-                    group.getPath(), 
-                    group.getCreated()
-                );
-                ret.add(org);   
-            }
-            
-            return ret;
-        }
-
-        
-        @Override
-        public void removeOrg(Org keeper, Org duplicate) throws Exception {
-            
-            // rename org so that it is no longer a duplicate
-            EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-            em.delete( new SimpleEntityRef( "group", duplicate.getId() ));
-            logger.info("Deleted org {}:{}", new Object[] { duplicate.getName(), duplicate.getId() });
-
-            // fix the org name index
-            OrganizationInfo orgInfoKeeper = managementService.getOrganizationByUuid( keeper.getId() );
-            try {
-                managementService.updateOrganizationUniqueIndex( orgInfoKeeper, duplicate.getId() );
-                logger.info("Updated index for keeper {}:{} not dup {}", new Object[] {
-                        orgInfoKeeper.getName(), orgInfoKeeper.getUuid(), duplicate.getId() });
-                
-            } catch ( Exception e ) {
-                // if there are multiple duplicates this will fail for all but one of them. That's OK
-                logger.warn("Error repairing unique value keeper {} duplicate {}", 
-                        keeper.getId(), duplicate.getId());
-            }
-        }
-        
-
-        @Override
-        public Set<OrgUser> getOrgUsers(Org org) throws Exception {
-            
-            Set<OrgUser> ret = new HashSet<OrgUser>();
-            
-            List<UserInfo> userInfos = managementService.getAdminUsersForOrganization( org.getId() );
-            
-            for ( UserInfo userInfo : userInfos ) {
-                OrgUser orgUser = new OrgUser( userInfo.getUuid(), userInfo.getUsername() );
-                ret.add(orgUser);
-            }
-            
-            return ret;
-        }
-
-        
-        @Override
-        public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
-            // forcefully remove admin user from org
-            managementService.removeAdminUserFromOrganization( user.getId(), org.getId(), true );
-            logger.info("Removed user {}:{} from org {}:{}", new Object[] {
-                    user.getName(), user.getId(), org.getName(), org.getId() });
-        }
-
-        
-        @Override
-        public void addUserToOrg(OrgUser user, Org org) throws Exception {
-            UserInfo userInfo = managementService.getAdminUserByUsername( user.getName() );
-            OrganizationInfo orgInfo = managementService.getOrganizationByUuid( org.getId() );
-            managementService.addAdminUserToOrganization( userInfo, orgInfo, false );
-            logger.info("Added user {}:{} to org {}:{}", new Object[] {
-                    user.getName(), user.getId(), org.getName(), org.getId() });
-        }
-
-        
-        @Override
-        public Set<UUID> getOrgApps(Org org) throws Exception {
-            BiMap<UUID, String> apps = managementService.getApplicationsForOrganization( org.getId() );
-            return apps.keySet();
-        }
-
-        
-        @Override
-        public void removeAppFromOrg(UUID appId, Org org) throws Exception {
-            managementService.removeOrganizationApplication( org.getId(), appId );
-            logger.info("Removed app {} from org {}:{}", new Object[] {
-                    appId, org.getName(), org.getId() });
-        }
-
-        
-        @Override
-        public void addAppToOrg(UUID appId, Org org) throws Exception {
-            managementService.addApplicationToOrganization( org.getId(), appId );
-            logger.info("Added app {} to org {}:{}", new Object[] {
-                    appId, org.getName(), org.getId() });
-        }
-
-        
-        @Override
-        public void logDuplicates(Map<String, Set<Org>> duplicatesByName) {
 
-            for ( String orgName : duplicatesByName.keySet() ) {
-                Set<Org> orgs = duplicatesByName.get(orgName);
-                for ( Org org : orgs ) {
-                    Entity orgEntity = (Entity)org.sourceValue;
+    static class DryRunManager extends UserOrgManager {
 
-                    StringBuilder sb = new StringBuilder();
-                    sb.append(orgEntity.toString()).append(", ");
-                    
-                    try {
-                        BiMap<UUID, String> apps = 
-                            managementService.getApplicationsForOrganization( orgEntity.getUuid() );
-                        String sep = "";
-                        for ( UUID uuid : apps.keySet() ) {
-                            String appName = apps.get(uuid);
-                            sb.append(appName).append(":").append(uuid).append(sep);
-                            sep = ", ";
-                        }
-                        
-                    } catch (Exception e) {
-                        logger.error("Error getting applications for org {}:{}", org.getName(), org.getId() );
-                    }
-                    
-                    logger.info(sb.toString());
-                }
-            }
+        public DryRunManager(EntityManagerFactory emf, ManagementService managementService ) {
+            super(emf, managementService);
         }
-
-        
-        @Override
-        public Org getOrg(UUID uuid) throws Exception {
-
-            EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
-            Entity entity = em.get( uuid );
-
-            Org org = new Org(
-                    entity.getUuid(),
-                    entity.getProperty( "path" )+"",
-                    entity.getCreated() );
-            org.sourceValue = entity;
-            
-            return org;
-        }
-    }
-
-    
-    class DryRunManager extends RepairManager {
         
         @Override
         public void removeUserFromOrg(OrgUser user, Org org) throws Exception {

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
new file mode 100644
index 0000000..b756f32
--- /dev/null
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgInterface.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+import rx.Observable;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+
+/**
+ * Mockable abstraction of user-org management.
+ */
+interface UserOrgInterface {
+    
+    Observable<Org> getOrgs() throws Exception;
+    
+    Observable<OrgUser> getUsers() throws Exception;
+
+    Set<Org> getUsersOrgs(OrgUser user) throws Exception;
+
+    Set<OrgUser> getOrgUsers(Org org ) throws Exception;
+    
+    void removeOrg(Org keeper, Org duplicate) throws Exception;
+
+    void removeUserFromOrg( OrgUser user, Org org ) throws Exception;
+    
+    void addUserToOrg( OrgUser user, Org org ) throws Exception;
+
+    Set<UUID> getOrgApps(Org org) throws Exception;
+    
+    void removeAppFromOrg( UUID appId, Org org ) throws Exception;
+    
+    void addAppToOrg( UUID appId, Org org ) throws Exception;
+  
+    void logDuplicates(Map<String, Set<Org>> duplicatesByName);
+
+    Org getOrg(UUID id ) throws Exception;
+    
+    OrgUser getOrgUser(UUID id ) throws Exception;
+
+    OrgUser lookupOrgUserByUsername( String username ) throws Exception;
+    
+    OrgUser lookupOrgUserByEmail( String email ) throws Exception;
+    
+    void removeOrgUser( OrgUser orgUser ) throws Exception;
+
+    void updateOrgUser(OrgUser targetUserEntity) throws Exception;
+
+    void setOrgUserName(OrgUser other, String newUserName) throws Exception;
+
+    class Org implements Comparable<Org> {
+        private UUID id;
+        private String name;
+        private long created;
+        public Object sourceValue;
+
+        public Org( UUID id, String name, long created) {
+            this.id = id;
+            this.name = name;
+            this.created = created;
+            this.created = System.currentTimeMillis();
+        }
+
+        public Org( UUID id, String name) {
+            this( id, name, System.currentTimeMillis()); 
+        }
+        
+        @Override
+        public boolean equals( Object o ) {
+            if ( o instanceof Org ) {
+                Org other = (Org)o;
+                return getId().equals( other.getId() );
+            }
+            return false;
+        }
+
+        @Override
+        public int compareTo(Org o) {
+            return Long.compare( this.created, o.created );
+        }
+
+        public UUID getId() {
+            return id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public long getCreated() {
+            return created;
+        }
+    }
+    
+    class OrgUser implements Comparable<OrgUser> {
+        private UUID id;
+        private String username;
+        private String email;
+        private long created;
+        public Object sourceValue;
+
+        public OrgUser( UUID id, String name, String email, long created ) {
+            this.id = id;
+            this.username = name;
+            this.email = email;
+            this.created = created;
+        }
+        
+        public OrgUser( UUID id, String name, String email ) {
+            this( id, name, email, System.currentTimeMillis());
+        }
+
+        public UUID getId() {
+            return id;
+        }
+
+        public String getEmail() {
+            return email;
+        }
+
+        public String getUsername() {
+            return username;
+        }
+        
+        public void setUsername( String username ) {
+            this.username = username;
+        }
+
+        public long getCreated() {
+            return created;
+        }
+        
+        @Override
+        public int compareTo(OrgUser o) {
+            return Long.compare( this.created, o.created );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
new file mode 100644
index 0000000..b18b8ca
--- /dev/null
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/UserOrgManager.java
@@ -0,0 +1,364 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+import com.google.common.collect.BiMap;
+import org.apache.usergrid.management.ManagementService;
+import org.apache.usergrid.management.OrganizationInfo;
+import org.apache.usergrid.management.UserInfo;
+import org.apache.usergrid.persistence.*;
+import org.apache.usergrid.persistence.cassandra.CassandraService;
+import org.apache.usergrid.persistence.entities.Group;
+import org.apache.usergrid.persistence.entities.User;
+import org.apache.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import rx.Observable;
+import rx.Subscriber;
+
+import java.util.*;
+
+
+class UserOrgManager implements UserOrgInterface {
+
+    static final Logger logger = LoggerFactory.getLogger( UserOrgManager.class );
+    
+    EntityManagerFactory emf;
+    ManagementService managementService;
+
+    public UserOrgManager(EntityManagerFactory emf, ManagementService managementService) {
+        this.emf = emf;
+        this.managementService = managementService;
+    }
+
+    @Override
+    public Observable<Org> getOrgs() throws Exception {
+
+        return Observable.create( new Observable.OnSubscribe<Org>() {
+
+            @Override
+            public void call(Subscriber<? super Org> subscriber) {
+                subscriber.onStart();
+                try {
+                    int count = 0;
+
+                    Query query = new Query();
+                    query.setLimit( ToolBase.MAX_ENTITY_FETCH );
+                    query.setResultsLevel( Results.Level.ALL_PROPERTIES );
+                    EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+                    Results results = em.searchCollection( em.getApplicationRef(), "groups", query );
+
+                    while (results.size() > 0) {
+                        for (Entity orgEntity : results.getList()) {
+
+                            Org org = new Org(
+                                    orgEntity.getUuid(),
+                                    orgEntity.getProperty( "path" ) + "",
+                                    orgEntity.getCreated() );
+                            org.sourceValue = orgEntity;
+
+                            subscriber.onNext( org );
+
+                            if (count++ % 1000 == 0) {
+                                logger.info( "Emitted {} orgs", count );
+                            }
+                            
+                        }
+                        if (results.getCursor() == null) {
+                            break;
+                        }
+                        query.setCursor( results.getCursor() );
+                        results = em.searchCollection( em.getApplicationRef(), "groups", query );
+                    }
+
+                } catch (Exception e) {
+                    subscriber.onError( e );
+                }
+                subscriber.onCompleted();
+            }
+        } );
+    }
+
+    @Override
+    public Observable<OrgUser> getUsers() throws Exception {
+
+        return Observable.create( new Observable.OnSubscribe<OrgUser>() {
+
+            @Override
+            public void call(Subscriber<? super OrgUser> subscriber) {
+                subscriber.onStart();
+                try {
+                    int count = 0;
+
+                    Query query = new Query();
+                    query.setLimit( ToolBase.MAX_ENTITY_FETCH );
+                    query.setResultsLevel( Results.Level.ALL_PROPERTIES );
+                    EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+                    Results results = em.searchCollection( em.getApplicationRef(), "users", query );
+
+                    while (results.size() > 0) {
+                        for (Entity entity : results.getList()) {
+
+                            OrgUser orgUser = new OrgUser(
+                                    entity.getUuid(),
+                                    entity.getProperty( "username" ) + "",
+                                    entity.getProperty( "email" ) + "",
+                                    entity.getCreated()
+                            );
+                            orgUser.sourceValue = entity;
+
+                            subscriber.onNext( orgUser );
+
+                            if (count++ % 1000 == 0) {
+                                logger.info( "Emitted {} users", count );
+                            }
+                        }
+                        if (results.getCursor() == null) {
+                            break;
+                        }
+                        query.setCursor( results.getCursor() );
+                        results = em.searchCollection( em.getApplicationRef(), "users", query );
+                    }
+
+                } catch (Exception e) {
+                    subscriber.onError( e );
+                }
+                subscriber.onCompleted();
+            }
+        } );
+    }
+
+    @Override
+    public Set<Org> getUsersOrgs(OrgUser user) throws Exception {
+
+        Set<Org> ret = new HashSet<Org>();
+
+        Map<String, Object> orgData = managementService.getAdminUserOrganizationData( user.getId() );
+
+        Map<String, Object> orgs = (Map<String, Object>) orgData.get( "organizations" );
+        for (String orgName : orgs.keySet()) {
+
+            Map<String, Object> orgMap = (Map<String, Object>) orgs.get( orgName );
+            Group group = managementService.getOrganizationProps(
+                    UUID.fromString( orgMap.get( "uuid" ).toString() ) );
+
+            Org org = new Org(
+                    group.getUuid(),
+                    group.getPath(),
+                    group.getCreated()
+            );
+            ret.add( org );
+        }
+
+        return ret;
+    }
+
+
+    @Override
+    public void removeOrg(Org keeper, Org duplicate) throws Exception {
+
+        // rename org so that it is no longer a duplicate
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        em.delete( new SimpleEntityRef( "group", duplicate.getId() ) );
+        logger.info( "Deleted org {}:{}", new Object[]{duplicate.getName(), duplicate.getId()} );
+
+        // fix the org name index
+        OrganizationInfo orgInfoKeeper = managementService.getOrganizationByUuid( keeper.getId() );
+        try {
+            managementService.updateOrganizationUniqueIndex( orgInfoKeeper, duplicate.getId() );
+            logger.info( "Updated index for keeper {}:{} not dup {}", new Object[]{
+                    orgInfoKeeper.getName(), orgInfoKeeper.getUuid(), duplicate.getId()} );
+
+        } catch (Exception e) {
+            // if there are multiple duplicates this will fail for all but one of them. That's OK
+            logger.warn( "Error repairing unique value keeper {} duplicate {}",
+                    keeper.getId(), duplicate.getId() );
+        }
+    }
+
+
+    @Override
+    public Set<OrgUser> getOrgUsers(Org org) throws Exception {
+
+        Set<OrgUser> ret = new HashSet<OrgUser>();
+
+        List<UserInfo> userInfos = managementService.getAdminUsersForOrganization( org.getId() );
+
+        for (UserInfo userInfo : userInfos) {
+            OrgUser orgUser = new OrgUser( userInfo.getUuid(), userInfo.getUsername(), userInfo.getEmail() );
+            ret.add( orgUser );
+        }
+
+        return ret;
+    }
+
+
+    @Override
+    public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
+        // forcefully remove admin user from org
+        managementService.removeAdminUserFromOrganization( user.getId(), org.getId(), true );
+        logger.info( "Removed user {}:{} from org {}:{}", new Object[]{
+                user.getUsername(), user.getId(), org.getName(), org.getId()} );
+    }
+
+
+    @Override
+    public void addUserToOrg(OrgUser user, Org org) throws Exception {
+        UserInfo userInfo = managementService.getAdminUserByUsername( user.getUsername() );
+        OrganizationInfo orgInfo = managementService.getOrganizationByUuid( org.getId() );
+        managementService.addAdminUserToOrganization( userInfo, orgInfo, false );
+        logger.info( "Added user {}:{} to org {}:{}", new Object[]{
+                user.getUsername(), user.getId(), org.getName(), org.getId()} );
+    }
+
+
+    @Override
+    public Set<UUID> getOrgApps(Org org) throws Exception {
+        BiMap<UUID, String> apps = managementService.getApplicationsForOrganization( org.getId() );
+        return apps.keySet();
+    }
+
+
+    @Override
+    public void removeAppFromOrg(UUID appId, Org org) throws Exception {
+        managementService.removeOrganizationApplication( org.getId(), appId );
+        logger.info( "Removed app {} from org {}:{}", new Object[]{
+                appId, org.getName(), org.getId()} );
+    }
+
+
+    @Override
+    public void addAppToOrg(UUID appId, Org org) throws Exception {
+        managementService.addApplicationToOrganization( org.getId(), appId );
+        logger.info( "Added app {} to org {}:{}", new Object[]{
+                appId, org.getName(), org.getId()} );
+    }
+
+
+    @Override
+    public void logDuplicates(Map<String, Set<Org>> duplicatesByName) {
+
+        for (String orgName : duplicatesByName.keySet()) {
+            Set<Org> orgs = duplicatesByName.get( orgName );
+            for (Org org : orgs) {
+                Entity orgEntity = (Entity) org.sourceValue;
+
+                StringBuilder sb = new StringBuilder();
+                sb.append( orgEntity.toString() ).append( ", " );
+
+                try {
+                    BiMap<UUID, String> apps =
+                            managementService.getApplicationsForOrganization( orgEntity.getUuid() );
+                    String sep = "";
+                    for (UUID uuid : apps.keySet()) {
+                        String appName = apps.get( uuid );
+                        sb.append( appName ).append( ":" ).append( uuid ).append( sep );
+                        sep = ", ";
+                    }
+
+                } catch (Exception e) {
+                    logger.error( "Error getting applications for org {}:{}", org.getName(), org.getId() );
+                }
+
+                logger.info( sb.toString() );
+            }
+        }
+    }
+
+
+    @Override
+    public Org getOrg(UUID uuid) throws Exception {
+
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        Entity entity = em.get( uuid );
+
+        Org org = new Org(
+                entity.getUuid(),
+                entity.getProperty( "path" ) + "",
+                entity.getCreated() );
+        org.sourceValue = entity;
+
+        return org;
+    }
+
+    @Override
+    public OrgUser getOrgUser(UUID uuid) throws Exception {
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        Entity entity = em.get( uuid );
+        
+        OrgUser user = new OrgUser( 
+            entity.getUuid(),
+            entity.getType(),
+            entity.getProperty("email")+"",
+            entity.getCreated()
+        );
+       
+        return user;
+    }
+
+
+    @Override
+    public OrgUser lookupOrgUserByUsername(String username) throws Exception {
+        UserInfo info = managementService.getAdminUserByUsername( username );
+        return info == null ? null : getOrgUser( info.getUuid() );
+    }
+
+    
+    @Override
+    public OrgUser lookupOrgUserByEmail(String email) throws Exception {
+        UserInfo info = managementService.getAdminUserByEmail( email );
+        return info == null ? null : getOrgUser( info.getUuid() );
+    }
+
+    
+    @Override
+    public void removeOrgUser(OrgUser orgUser) throws Exception {
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        em.delete( new SimpleEntityRef( "user", orgUser.getId() ));
+    }
+
+    
+    @Override
+    public void updateOrgUser(OrgUser targetUserEntity ) throws Exception {
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        User user = em.get(targetUserEntity.getId(), User.class);
+        user.setUsername( targetUserEntity.getUsername() );
+        user.setEmail( targetUserEntity.getEmail() );
+        em.update( user );
+    }
+
+    
+    @Override
+    public void setOrgUserName(OrgUser other, String newUserName ) throws Exception {
+        
+        EntityManager em = emf.getEntityManager( CassandraService.MANAGEMENT_APPLICATION_ID );
+        
+        logger.info( "Setting username to {} for user with username {} and id {}", new Object[] {
+                newUserName, other.getUsername(), other.getId()
+        } );
+
+        try {
+            em.setProperty( new SimpleEntityRef( "user", other.getId() ), "username", newUserName, true );
+        }
+        catch ( DuplicateUniquePropertyExistsException e ) {
+            logger.warn( "More than 1 user has the username of {}.  Setting the username to their username+UUID as a "
+                    + "fallback", newUserName );
+
+            setOrgUserName( other, String.format( "%s-%s", other.getUsername(), other.getId() ) );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
new file mode 100644
index 0000000..1ba3766
--- /dev/null
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateAdminUserRepairTest.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.usergrid.tools;
+
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.usergrid.ServiceITSetup;
+import org.apache.usergrid.ServiceITSetupImpl;
+import org.apache.usergrid.ServiceITSuite;
+import org.apache.usergrid.management.OrganizationOwnerInfo;
+import org.junit.ClassRule;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.UUID;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertNotNull;
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Test duplicate org repair.
+ */
+public class DuplicateAdminUserRepairTest {
+    
+    static final Logger logger = LoggerFactory.getLogger( DuplicateAdminUserRepairTest.class );
+    
+    @ClassRule
+    public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource );
+
+    
+    /**
+     * Test tool logic with mock manager that returns duplicates
+     */
+    @org.junit.Test
+    public void testMockWithDups() throws Exception {
+
+        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
+        MockManager mockManager = new MockManager();
+        dor.manager = mockManager;
+        dor.testing = true;
+
+        // the MockManager creates 2 pairs of duplicate orgs
+        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+
+        // verify that correct users indexed
+        usersIndexed( mockManager );
+        
+        dor.startTool( new String[] {}, false ); // false means do not call System.exit()
+
+        // verify that correct users indexed
+        usersIndexed( mockManager );
+
+        // verify that duplicate users are gone (the "a" users were the first ones created)
+        assertNull( "must remove user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNull( "must remove user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+
+        // and keepers survived
+        assertNotNull( "user1_b survived", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_b survived", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+    }
+
+    
+    private void usersIndexed( MockManager mockManager ) {
+        assertEquals("user1_b is in the index",
+                mockManager.lookupOrgUserByUsername(mockManager.user1_b.getUsername()).getId(),
+                mockManager.user1_b.getId() );
+
+        assertEquals("user1_b is in the index",
+                mockManager.lookupOrgUserByEmail(mockManager.user1_b.getEmail()).getId(),
+                mockManager.user1_b.getId() );
+
+        assertEquals("user2_b is in the index",
+                mockManager.lookupOrgUserByUsername(mockManager.user2_b.getUsername()).getId(),
+                mockManager.user2_b.getId() );
+
+        assertEquals("user2_b is in the index",
+                mockManager.lookupOrgUserByEmail(mockManager.user2_b.getEmail()).getId(),
+                mockManager.user2_b.getId() ); 
+    }
+    
+
+    @org.junit.Test
+    public void testDryRun() throws Exception {
+
+        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
+        MockManager mockManager = new MockManager();
+        dor.manager = mockManager;
+        dor.testing = true;
+
+        // the MockManager creates 2 pairs of duplicate orgs
+        assertNotNull( "user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));
+
+        // verify that correct users indexed
+        usersIndexed( mockManager );
+
+        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
+
+        // verify that correct users indexed
+        usersIndexed( mockManager );
+        
+        // insure nothng was deleted by dry-run
+        assertNotNull( "dryrun should not delete user1_a", mockManager.getOrgUser( mockManager.user1_a.getId() ));
+        assertNotNull( "dryrun should not delete user1_b", mockManager.getOrgUser( mockManager.user1_b.getId() ));
+        assertNotNull( "dryrun should not delete user2_a", mockManager.getOrgUser( mockManager.user2_a.getId() ));
+        assertNotNull( "dryrun should not delete user2_b", mockManager.getOrgUser( mockManager.user2_b.getId() ));  
+    }
+
+    
+    /**
+     * Smoke test: does "real" manager run without throwing exceptions?
+     */
+    @org.junit.Test
+    public void testManagerNoDups() throws Exception {
+
+        // create two orgs each with owning user
+
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random2, "user_" + random2, "user_" + random2,
+                "user_" + random2 + "@example.com", "password" );
+
+        // Add user1 to org2
+
+        setup.getMgmtSvc().addAdminUserToOrganization(
+                orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false );
+
+        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair();
+
+        dor.startTool( new String[]{}, false );  // false means do not call System.exit()
+
+        dor.startTool( new String[]{"dryrun", "true"}, false ); // false means do not call System.exit()
+
+        assertTrue( true ); // we're happy if we get to this point
+    }
+
+    
+    @org.junit.Test
+    public void testManagerMethods() throws Exception {
+        
+        // create two orgs each with owning user
+
+        final String random1 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
+
+        final String random2 = org.apache.commons.lang.RandomStringUtils.randomAlphanumeric( 10 );
+        final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
+                "org_" + random2, "user_" + random2, "user_" + random2,
+                "user_" + random2 + "@example.com", "password" );
+
+        DuplicateAdminUserRepair dor = new DuplicateAdminUserRepair(setup.getEmf(), setup.getMgmtSvc());
+        dor.manager = dor.createNewRepairManager(); // test the real manager 
+
+        // start the tool so that Spring, Cassandra, etc/ gets initialized
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
+
+        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() ));
+        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
+
+        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() )); 
+        dor.manager.removeOrgUser( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
+        
+        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo1.getOwner().getUuid() ));
+        assertNotNull( dor.manager.getOrgUser( orgOwnerInfo2.getOwner().getUuid() ));
+    }
+    
+    
+    static class MockManager extends MockUserOrgManager {
+
+        OrgUser user1_a;
+        OrgUser user1_b;
+        OrgUser user2_a;
+        OrgUser user2_b;
+        
+        public MockManager() throws Exception {
+            
+            super(1); // ask parent to create one pair of duplicate orgs
+            
+            Org org = orgsById.values().iterator().next();
+          
+            String sfx = RandomStringUtils.randomAlphanumeric(10);
+
+            // user1 a and b have duplicate usernames AND DUPLICATE EMAIL ADDRESSES
+            
+            user1_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user1_a, org );
+            pause(100);
+
+            user1_b = createOrgUser( UUID.randomUUID(), "Username_"+sfx, "Username_"+sfx+"@example.com" );
+            addUserToOrg( user1_b, org );
+            pause(100);
+
+            // user2 a and b have duplicate usernames AND DIFFERENT EMAIL ADDRESSES 
+            
+            user2_a = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user2_a, org );
+            pause(100);
+
+            user2_b = createOrgUser( UUID.randomUUID(), "UserName_"+sfx, "UserName_"+sfx+"@example.com" );
+            addUserToOrg( user2_b, org );
+            pause(100);
+
+        }
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/usergrid/blob/7324b5a8/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java
index 8726a33..1cb8a9e 100644
--- a/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java
+++ b/stack/tools/src/test/java/org/apache/usergrid/tools/DuplicateOrgRepairTest.java
@@ -32,8 +32,8 @@ import rx.functions.Action1;
 import java.util.*;
 
 import static junit.framework.Assert.assertTrue;
-import static org.apache.usergrid.tools.DuplicateOrgInterface.Org;
-import static org.apache.usergrid.tools.DuplicateOrgInterface.OrgUser;
+import static org.apache.usergrid.tools.UserOrgInterface.Org;
+import static org.apache.usergrid.tools.UserOrgInterface.OrgUser;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
@@ -43,11 +43,11 @@ import static org.junit.Assert.fail;
  */
 public class DuplicateOrgRepairTest {
     static final Logger logger = LoggerFactory.getLogger( DuplicateOrgRepairTest.class );
-    
+
     @ClassRule
     public static ServiceITSetup setup = new ServiceITSetupImpl( ServiceITSuite.cassandraResource );
 
-    
+
     /**
      * Test tool logic with mock manager that returns duplicates
      */
@@ -57,34 +57,34 @@ public class DuplicateOrgRepairTest {
         int numOrgs = 10; // create 10 orgs and a dup for each
 
         final DuplicateOrgRepair dor = new DuplicateOrgRepair();
-        
-        dor.manager = new Manager( numOrgs );
 
-        assertEquals( "must start with dups", 
-                2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single());
+        dor.manager = new MockUserOrgManager( numOrgs );
 
-        dor.startTool( new String[] {}, false ); // false means do not call System.exit()
+        assertEquals( "must start with dups",
+                2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
 
-        assertEquals( "must remove dups", 
-                numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single());
+        dor.startTool( new String[]{}, false ); // false means do not call System.exit()
+
+        assertEquals( "must remove dups",
+                numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
 
         checkOrgsDeduped( dor );
     }
 
 
     @org.junit.Test
-    public void testMockWithOneDupoDryRun() throws Exception {
+    public void testMockWithOneDupsDryRun() throws Exception {
 
         int numOrgs = 1; // create 1 org and a dup
 
         DuplicateOrgRepair dor = new DuplicateOrgRepair();
         dor.testing = true;
-        dor.manager = new Manager( numOrgs );
+        dor.manager = new MockUserOrgManager( numOrgs );
 
         assertEquals( "must start with dups",
                 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
 
-        Iterator<Org> orgIter = ((Manager) dor.manager).usersByOrg.keySet().iterator();
+        Iterator<Org> orgIter = ((MockUserOrgManager) dor.manager).usersByOrg.keySet().iterator();
         Org org1 = orgIter.next();
         Org org2 = orgIter.next();
         dor.startTool( new String[]{
@@ -96,8 +96,8 @@ public class DuplicateOrgRepairTest {
         assertEquals( "dry-run should not remove dups",
                 2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
     }
-    
-    
+
+
     @org.junit.Test
     public void testMockWithOneDup() throws Exception {
 
@@ -105,40 +105,40 @@ public class DuplicateOrgRepairTest {
 
         DuplicateOrgRepair dor = new DuplicateOrgRepair();
         dor.testing = true;
-        dor.manager = new Manager( numOrgs );
+        dor.manager = new MockUserOrgManager( numOrgs );
 
-        Iterator<Org> orgIter = ((Manager)dor.manager).usersByOrg.keySet().iterator();
+        Iterator<Org> orgIter = ((MockUserOrgManager) dor.manager).usersByOrg.keySet().iterator();
         Org org1 = orgIter.next();
         Org org2 = orgIter.next();
-        dor.startTool( new String[] {
-             "-org1", org1.getId()+"",
-             "-org2", org2.getId()+"",
+        dor.startTool( new String[]{
+                "-org1", org1.getId() + "",
+                "-org2", org2.getId() + "",
         }, false ); // false means do not call System.exit()
 
         assertEquals( "must remove dups",
-                numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single());
+                numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
 
         checkOrgsDeduped( dor );
     }
 
-    
+
     private void checkOrgsDeduped(final DuplicateOrgRepair dor) throws Exception {
         dor.manager.getOrgs().doOnNext( new Action1<Org>() {
             @Override
             public void call(Org org) {
                 try {
-                    assertEquals("remaining orgs should have right number of users",
-                            3, dor.manager.getOrgUsers(org).size());
+                    assertEquals( "remaining orgs should have right number of users",
+                            3, dor.manager.getOrgUsers( org ).size() );
 
-                    assertEquals("remaining orgs should have right number of apps",
-                            3, dor.manager.getOrgApps(org).size());
+                    assertEquals( "remaining orgs should have right number of apps",
+                            3, dor.manager.getOrgApps( org ).size() );
 
                 } catch (Exception e) {
-                    logger.error("Error counting apps or users: " + e.getMessage(), e);
-                    fail("Error counting apps or users");
+                    logger.error( "Error counting apps or users: " + e.getMessage(), e );
+                    fail( "Error counting apps or users" );
                 }
             }
-        }).toBlocking().lastOrDefault( null );
+        } ).toBlocking().lastOrDefault( null );
     }
 
 
@@ -148,18 +148,18 @@ public class DuplicateOrgRepairTest {
         int numOrgs = 10; // create 10 orgs and a dup for each
 
         DuplicateOrgRepair dor = new DuplicateOrgRepair();
-        dor.manager = new Manager( numOrgs );
+        dor.manager = new MockUserOrgManager( numOrgs );
 
-        assertEquals( "must start with dups", 
-                2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single());
+        assertEquals( "must start with dups",
+                2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
 
-        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
 
-        assertEquals( "must detect right number of dups", 
+        assertEquals( "must detect right number of dups",
                 numOrgs, dor.duplicatesByName.keySet().size() );
-        
-        assertEquals( "dryrun must not remove dups", 
-                2 * numOrgs, (long)dor.manager.getOrgs().count().toBlocking().single());
+
+        assertEquals( "dryrun must not remove dups",
+                2 * numOrgs, (long) dor.manager.getOrgs().count().toBlocking().single() );
     }
 
 
@@ -185,17 +185,17 @@ public class DuplicateOrgRepairTest {
 
         setup.getMgmtSvc().addAdminUserToOrganization(
                 orgOwnerInfo1.getOwner(), orgOwnerInfo2.getOrganization(), false );
-        
+
         DuplicateOrgRepair dor = new DuplicateOrgRepair();
-        
-        dor.startTool( new String[] {}, false );  // false means do not call System.exit()
-        
-        dor.startTool( new String[] { "dryrun", "true" }, false ); // false means do not call System.exit()
 
-        assertTrue(true); // we're happy if we get to this point
+        dor.startTool( new String[]{}, false );  // false means do not call System.exit()
+
+        dor.startTool( new String[]{"dryrun", "true"}, false ); // false means do not call System.exit()
+
+        assertTrue( true ); // we're happy if we get to this point
     }
 
-    
+
     @org.junit.Test
     public void testManagerAddUserToOrg() throws Exception {
 
@@ -211,43 +211,41 @@ public class DuplicateOrgRepairTest {
                 "org_" + random2, "user_" + random2, "user_" + random2,
                 "user_" + random2 + "@example.com", "password" );
 
-        DuplicateOrgRepair dor = new DuplicateOrgRepair();
+        DuplicateOrgRepair dor = new DuplicateOrgRepair(setup.getEmf(), setup.getMgmtSvc());
         dor.manager = dor.createNewRepairManager(); // test the real manager 
 
-        // start the tool so thaht Spring, Cassandra, etc/ gets initialized
-        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
-        
+        // start the tool so that Spring, Cassandra, etc/ gets initialized
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
+
         Org org1 = new Org(
-            orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L);
+                orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L );
 
         Org org2 = new Org(
-            orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L);
+                orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L );
 
         OrgUser user1 = new OrgUser(
-                orgOwnerInfo1.getOwner().getUuid(), orgOwnerInfo1.getOwner().getUsername());
-
-        OrgUser user2 = new OrgUser(
-                orgOwnerInfo2.getOwner().getUuid(), orgOwnerInfo2.getOwner().getUsername());
+                orgOwnerInfo1.getOwner().getUuid(),
+                orgOwnerInfo1.getOwner().getUsername(),
+                orgOwnerInfo1.getOwner().getEmail() );
 
-        assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size());
-        assertEquals( 1, dor.manager.getOrgUsers( org1 ).size());
-        assertEquals( 1, dor.manager.getOrgUsers( org2 ).size());
+        assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size() );
+        assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() );
+        assertEquals( 1, dor.manager.getOrgUsers( org2 ).size() );
 
         dor.manager.addUserToOrg( user1, org2 );
 
-        assertEquals( 2, dor.manager.getUsersOrgs( user1 ).size());
+        assertEquals( 2, dor.manager.getUsersOrgs( user1 ).size() );
         assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() );
         assertEquals( 2, dor.manager.getOrgUsers( org2 ).size() );
 
         dor.manager.removeUserFromOrg( user1, org2 );
-        
-        assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size());
-        assertEquals( 1, dor.manager.getOrgUsers( org1 ).size());
+
+        assertEquals( 1, dor.manager.getUsersOrgs( user1 ).size() );
+        assertEquals( 1, dor.manager.getOrgUsers( org1 ).size() );
         assertEquals( 1, dor.manager.getOrgUsers( org2 ).size() );
     }
 
 
-
     @org.junit.Test
     public void testManagerAddAppToOrg() throws Exception {
 
@@ -255,38 +253,38 @@ public class DuplicateOrgRepairTest {
 
         final String random1 = RandomStringUtils.randomAlphanumeric( 10 );
         final OrganizationOwnerInfo orgOwnerInfo1 = setup.getMgmtSvc().createOwnerAndOrganization(
-            "org_" + random1, "user_" + random1, "user_" + random1,
-            "user_" + random1 + "@example.com", "password" );
+                "org_" + random1, "user_" + random1, "user_" + random1,
+                "user_" + random1 + "@example.com", "password" );
 
         final String random2 = RandomStringUtils.randomAlphanumeric( 10 );
         final OrganizationOwnerInfo orgOwnerInfo2 = setup.getMgmtSvc().createOwnerAndOrganization(
-            "org_" + random2, "user_" + random2, "user_" + random2,
-            "user_" + random2 + "@example.com", "password" );
+                "org_" + random2, "user_" + random2, "user_" + random2,
+                "user_" + random2 + "@example.com", "password" );
 
         // give org1 two apps
-        
+
         ApplicationInfo app11 = setup.getMgmtSvc().createApplication(
                 orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) );
 
-        ApplicationInfo app12= setup.getMgmtSvc().createApplication(
-            orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ));
+        ApplicationInfo app12 = setup.getMgmtSvc().createApplication(
+                orgOwnerInfo1.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) );
 
         // give org2 one app 
-        
+
         ApplicationInfo app21 = setup.getMgmtSvc().createApplication(
-            orgOwnerInfo2.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ));
+                orgOwnerInfo2.getOrganization().getUuid(), "app_" + RandomStringUtils.randomAlphanumeric( 10 ) );
 
-        DuplicateOrgRepair dor = new DuplicateOrgRepair();
+        DuplicateOrgRepair dor = new DuplicateOrgRepair(setup.getEmf(), setup.getMgmtSvc());
         dor.manager = dor.createNewRepairManager(); // test the real manager 
 
         // start the tool so that Spring, Cassandra, etc/ gets initialized
-        dor.startTool( new String[] { "-dryrun", "true" }, false ); // false means do not call System.exit()
+        dor.startTool( new String[]{"-dryrun", "true"}, false ); // false means do not call System.exit()
 
         Org org1 = new Org(
-            orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L);
+                orgOwnerInfo1.getOrganization().getUuid(), orgOwnerInfo1.getOrganization().getName(), 0L );
 
         Org org2 = new Org(
-            orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L);
+                orgOwnerInfo2.getOrganization().getUuid(), orgOwnerInfo2.getOrganization().getName(), 0L );
 
         assertEquals( 2, dor.manager.getOrgApps( org1 ).size() );
         assertEquals( 1, dor.manager.getOrgApps( org2 ).size() );
@@ -297,176 +295,10 @@ public class DuplicateOrgRepairTest {
         assertEquals( 1, dor.manager.getOrgApps( org2 ).size() );
 
         dor.manager.addAppToOrg( app12.getId(), org2 );
-        
+
         assertEquals( 1, dor.manager.getOrgApps( org1 ).size() );
         assertEquals( 2, dor.manager.getOrgApps( org2 ).size() );
     }
-    
-    
-    /**
-     * Mock manager implementation for mockTesting.
-     */
-    class Manager implements DuplicateOrgInterface {
-
-        Set<Org> orgs;
-
-        Set<OrgUser> orgUsers;
-
-        Map<Org, Set<OrgUser>> usersByOrg = new HashMap<Org, Set<OrgUser>>();
-
-        Map<OrgUser, Set<Org>> orgsByUser = new HashMap<OrgUser, Set<Org>>();
-
-        Map<Org, Set<UUID>> appsByOrg = new HashMap<Org, Set<UUID>>();
-        
-        Map<UUID, Org> orgsById = new HashMap<UUID, Org>();
-
-
-        /**
-         * Populate manager with orgs and users.
-         * Will create a number of orgs and a duplicate for each.
-         *
-         * @param numOrgs One half of the number of orgs to create.
-         */
-        public Manager(int numOrgs) {
-
-            for (int i = 0; i < numOrgs; i++) {
-
-                // each org name is duplicated once another org created 20 ms apart
-
-                Org org1 = new Org( UUID.randomUUID(), "org_" + i, System.currentTimeMillis() );
-                try {
-                    Thread.sleep( 100 );
-                } catch (InterruptedException intentionallyIgnored) {
-                }
-                Org org2 = new Org( UUID.randomUUID(), "org_" + i, System.currentTimeMillis() );
-
-                orgsById.put( org1.getId(), org1 );
-                orgsById.put( org2.getId(), org2 );
-
-                OrgUser usera = new OrgUser( UUID.randomUUID(), "user_" + i + "_a" );
-                OrgUser userb = new OrgUser( UUID.randomUUID(), "user_" + i + "_b" );
-                OrgUser userc = new OrgUser( UUID.randomUUID(), "user_" + i + "_c" );
-
-                // add users to orgs 
-
-                Set<OrgUser> org1Users = new HashSet<OrgUser>();
-                org1Users.add( usera );
-                org1Users.add( userb );
-                usersByOrg.put( org1, org1Users );
-
-                Set<OrgUser> org2Users = new HashSet<OrgUser>();
-                org2Users.add( userc );
-                usersByOrg.put( org2, org2Users );
-
-                // add orgs to users 
-
-                Set<Org> useraOrgs = new HashSet<Org>();
-                useraOrgs.add( org1 );
-                orgsByUser.put( usera, useraOrgs );
-
-                Set<Org> userbOrgs = new HashSet<Org>();
-                userbOrgs.add( org1 );
-                orgsByUser.put( userb, userbOrgs );
 
-                Set<Org> usercOrgs = new HashSet<Org>();
-                usercOrgs.add( org2 );
-                orgsByUser.put( userc, usercOrgs );
+}    
 
-                // add some apps to the orgs
-
-                Set<UUID> org1apps = new HashSet<UUID>();
-                org1apps.add( UUID.randomUUID() );
-                org1apps.add( UUID.randomUUID() );
-                appsByOrg.put( org1, org1apps );
-
-                Set<UUID> org2apps = new HashSet<UUID>();
-                org2apps.add( UUID.randomUUID() );
-                appsByOrg.put( org2, org2apps );
-            }
-        }
-
-
-        @Override
-        public Observable<Org> getOrgs() throws Exception {
-            return Observable.from( usersByOrg.keySet() );
-        }
-
-        @Override
-        public Observable<OrgUser> getUsers() throws Exception {
-            return Observable.from( orgsByUser.keySet() );
-        }
-
-        @Override
-        public Set<Org> getUsersOrgs(OrgUser user) {
-            return orgsByUser.get( user );
-        }
-
-        @Override
-        public void removeOrg(Org keeper, Org duplicate) throws Exception {
-            Set<OrgUser> users = usersByOrg.get( duplicate );
-            for (OrgUser user : users) {
-                Set<Org> userOrgs = orgsByUser.get( user );
-                userOrgs.remove( duplicate );
-            }
-            usersByOrg.remove( duplicate );
-        }
-
-        @Override
-        public Set<OrgUser> getOrgUsers(Org org) throws Exception {
-            return usersByOrg.get( org );
-        }
-
-        @Override
-        public void removeUserFromOrg(OrgUser user, Org org) throws Exception {
-            
-            Set<OrgUser> orgUsers = usersByOrg.get( org );
-            orgUsers.remove( user );
-            
-            Set<Org> usersOrgs = orgsByUser.get( user );
-            usersOrgs.remove( org );
-        }
-
-        @Override
-        public void addUserToOrg(OrgUser user, Org org) throws Exception {
-            
-            Set<Org> usersOrgs = orgsByUser.get( user );
-            usersOrgs.add( org );
-            
-            Set<OrgUser> orgsUsers = usersByOrg.get( org );
-            orgsUsers.add( user );
-        }
-
-        @Override
-        public Set<UUID> getOrgApps(Org org) {
-            return appsByOrg.get( org );
-        }
-
-        @Override
-        public void removeAppFromOrg(UUID appId, Org org) throws Exception {
-            Set<UUID> apps = appsByOrg.get( org );
-            apps.remove( appId );
-        }
-
-        @Override
-        public void addAppToOrg(UUID appId, Org org) throws Exception {
-            Set<UUID> apps = appsByOrg.get( org );
-            apps.add(appId); 
-        }
-
-        @Override
-        public void logDuplicates(Map<String, Set<Org>> duplicatesByName) {
-
-            for (String orgName : duplicatesByName.keySet()) {
-                Set<Org> orgs = duplicatesByName.get( orgName );
-                for (Org org : orgs) {
-                    logger.info( "Duplicate org {}:{}", orgName, org.getId() );
-                }
-            }
-        }
-
-        @Override
-        public Org getOrg(UUID uuid) throws Exception {
-            return orgsById.get(uuid); 
-        }
-    }
-}


[8/9] usergrid git commit: Another case-insensitivity fix.

Posted by sn...@apache.org.
Another case-insensitivity fix.


Project: http://git-wip-us.apache.org/repos/asf/usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/usergrid/commit/a3a8b898
Tree: http://git-wip-us.apache.org/repos/asf/usergrid/tree/a3a8b898
Diff: http://git-wip-us.apache.org/repos/asf/usergrid/diff/a3a8b898

Branch: refs/heads/1.x
Commit: a3a8b89851b2965c3f327318f28d4905d3a3d0f7
Parents: a1b6685
Author: Dave Johnson <sn...@apache.org>
Authored: Thu Jan 14 12:58:02 2016 -0500
Committer: Dave Johnson <sn...@apache.org>
Committed: Thu Jan 14 12:58:02 2016 -0500

----------------------------------------------------------------------
 .../java/org/apache/usergrid/tools/DuplicateAdminRepair.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/a3a8b898/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
----------------------------------------------------------------------
diff --git a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
index 79e403d..d63b8d6 100644
--- a/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
+++ b/stack/tools/src/main/java/org/apache/usergrid/tools/DuplicateAdminRepair.java
@@ -136,8 +136,8 @@ public class DuplicateAdminRepair extends ToolBase {
                     logger.warn( "User {} has no email", user.getId() );
                     return;
                 }
-                emails.put( user.getEmail(), user );
-                usernames.put( user.getEmail(), user );
+                emails.put( user.getEmail().toLowerCase(), user );
+                usernames.put( user.getUsername().toLowerCase(), user );
                 
             }
         } ).toBlocking().lastOrDefault( null );