org.usergrid.tools.DupOrgRepair.java Source code

Java tutorial

Introduction

Here is the source code for org.usergrid.tools.DupOrgRepair.java

Source

/*******************************************************************************
 * 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 java.io.FileWriter;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

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.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.utils.JsonUtils;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
 * 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 DupOrgRepair extends ExportingToolBase {

    /**
       * 
       */
    private static final int PAGE_SIZE = 100;

    private static final Logger logger = LoggerFactory.getLogger(DupOrgRepair.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");

        createDir(outputDir);

        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> orgs = HashMultimap.create();

        do {

            r = em.searchCollection(app, "groups", query);

            for (Entity entity : r.getEntities()) {
                String name = entity.getProperty("path").toString().toLowerCase();
                orgs.put(name, 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 name : orgs.keySet()) {
            Collection<UUID> ids = orgs.get(name);

            if (ids.size() > 1) {
                logger.warn("Found multiple orgs with the name {}", name);

                // look this up the same way the REST tier does. This way we will always
                // map the same way and the user will not notice a background merge
                OrganizationInfo orgInfo = managementService.getOrganizationByName(name);

                UUID targetOrgId = orgInfo.getUuid();

                ids.remove(targetOrgId);

                for (UUID sourceId : ids) {
                    mergeOrganizations(outputDir, sourceId, targetOrgId);
                }

            }
        }

        logger.info("Merge complete");

    }

    /**
     * Merge the source orgId into the targetId in the following way.
     * 
     * 1) link all admins from the source org to the target org 2) link all apps
     * from the source org to the target or 3) delete the target org
     * 
     * @param sourceOrgId
     * @param targetOrgId
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    private void mergeOrganizations(String outputDir, UUID sourceOrgId, UUID targetOrgId) throws Exception {

        OrganizationInfo sourceOrgInfo = managementService.getOrganizationByUuid(sourceOrgId);

        Map<String, Object> sourceOrg = managementService.getOrganizationData(sourceOrgInfo);

        OrganizationInfo targetOrgInfo = managementService.getOrganizationByUuid(targetOrgId);

        Map<String, Object> targetOrg = managementService.getOrganizationData(targetOrgInfo);

        // Dump the output on these two orgs
        FileWriter file = new FileWriter(
                String.format("%s/%s.%s.orig", outputDir, sourceOrgInfo.getName(), sourceOrgId));

        file.write(JsonUtils.mapToFormattedJsonString(sourceOrg));

        file.write("\n\n");

        file.write(JsonUtils.mapToFormattedJsonString(targetOrg));

        file.flush();
        file.close();

        // BiMap<UUID, String> targetApps =
        // managementService.getApplicationsForOrganization(targetOrgId);

        // now perform the merge

        // add all the admins
        Map<String, UserInfo> admins = (Map<String, UserInfo>) sourceOrg.get("users");

        for (Entry<String, UserInfo> adminEntry : admins.entrySet()) {
            UserInfo admin = adminEntry.getValue();

            logger.info("adding admin with uuid {} and email {} to org with name {} and uuid {}", new Object[] {
                    admin.getUuid(), admin.getEmail(), targetOrgInfo.getName(), targetOrgInfo.getUuid() });

            // copy the admins over
            managementService.addAdminUserToOrganization(admin, targetOrgInfo, false);
        }

        // get the root entity manager
        EntityManager em = emf.getEntityManager(CassandraService.MANAGEMENT_APPLICATION_ID);

        // Add the apps to the org
        Map<String, UUID> sourceApps = (Map<String, UUID>) sourceOrg.get("applications");

        Map<String, UUID> targetApps = (Map<String, UUID>) targetOrg.get("applications");

        for (Entry<String, UUID> app : sourceApps.entrySet()) {

            // we have colliding app names
            if (targetApps.get(app.getKey()) != null) {

                // already added, skip it
                if (app.getValue().equals(targetApps.get(app.getKey()))) {
                    continue;
                }

                // check to see if this orgname/appname lookup returns the app we're
                // about to re-assign. If it does NOT, then we need to rename this app
                // before performing the link.
                UUID appIdToKeep = emf.lookupApplication(app.getKey());

                UUID appIdToChange = appIdToKeep.equals(app.getValue()) ? targetApps.get(app.getKey())
                        : app.getValue();

                // get the existing target entity
                Entity appEntity = em.get(appIdToChange);

                if (appEntity != null) {

                    String oldName = appEntity.getProperty("name").toString();
                    String newName = oldName + appEntity.getUuid();

                    //force the property to be updated
                    em.setProperty(appEntity, "name", newName, true);

                    logger.info("Renamed app from {} to {}", oldName, newName);

                }
            }

            logger.info("Adding application with name {} and id {} to organization with uuid {}",
                    new Object[] { app.getKey(), app.getValue(), targetOrgId });
            managementService.addApplicationToOrganization(targetOrgId, app.getValue());
        }

        // now delete the original org

        logger.info("Deleting org with the name {} and uuid {}", sourceOrgInfo.getName(), sourceOrgInfo.getUuid());

        // delete the source org
        em.delete(new SimpleEntityRef("group", sourceOrgId));

        // re-dump the target from the cassandra stat
        targetOrgInfo = managementService.getOrganizationByUuid(targetOrgId);

        targetOrg = managementService.getOrganizationData(targetOrgInfo);

        file = new FileWriter(String.format("%s/%s.%s.new", outputDir, targetOrgInfo.getName(), targetOrgId));

        file.write(JsonUtils.mapToFormattedJsonString(targetOrg));

        file.flush();
        file.close();

    }
}