
org.usergrid.tools.DupAdminRepair Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of usergrid-tools Show documentation
Show all versions of usergrid-tools Show documentation
Command line tools for Usergrid system.
The newest version!
/*******************************************************************************
* Copyright 2012 Apigee Corporation
*
* Licensed 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.usergrid.tools;
import static org.usergrid.persistence.cassandra.CassandraService.MANAGEMENT_APPLICATION_ID;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import me.prettyprint.cassandra.serializers.ByteBufferSerializer;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usergrid.management.OrganizationInfo;
import org.usergrid.management.UserInfo;
import org.usergrid.persistence.Entity;
import org.usergrid.persistence.EntityManager;
import org.usergrid.persistence.EntityRef;
import org.usergrid.persistence.Query;
import org.usergrid.persistence.Results;
import org.usergrid.persistence.SimpleEntityRef;
import org.usergrid.persistence.cassandra.CassandraService;
import org.usergrid.persistence.entities.Application;
import org.usergrid.persistence.entities.User;
import org.usergrid.persistence.exceptions.DuplicateUniquePropertyExistsException;
import org.usergrid.utils.JsonUtils;
import org.usergrid.utils.UUIDUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
/**
* 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.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 emails = HashMultimap.create();
Multimap 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 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 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 tempIds = new ArrayList(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 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
*
* @param targetDir
* @param adminId
* @param userNames
* @throws Exception
*/
private void resolveUsernameConflicts(String targetDir, String userName, Collection 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
* @param em
* @param other
* @param newUserName
* @throws Exception
*/
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
*
* @param targetDir
* @param sourceId
* @param targetId
* @throws Exception
*/
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> sourceOrgs = (Map>) 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);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy