edu.internet2.middleware.changelogconsumer.googleapps.GoogleAppsFullSync.java Source code

Java tutorial

Introduction

Here is the source code for edu.internet2.middleware.changelogconsumer.googleapps.GoogleAppsFullSync.java

Source

/*
 * Licensed to the University Corporation for Advanced Internet Development,
 * Inc. (UCAID) under one or more contributor license agreements.  See the
 * NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The UCAID 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 edu.internet2.middleware.changelogconsumer.googleapps;

import com.google.api.services.admin.directory.model.Group;
import com.google.api.services.admin.directory.model.Member;
import com.google.api.services.admin.directory.model.User;
import edu.internet2.middleware.changelogconsumer.googleapps.cache.GoogleCacheManager;
import edu.internet2.middleware.changelogconsumer.googleapps.utils.ComparableGroupItem;
import edu.internet2.middleware.changelogconsumer.googleapps.utils.ComparableMemberItem;
import edu.internet2.middleware.changelogconsumer.googleapps.utils.GoogleAppsSyncProperties;
import edu.internet2.middleware.grouper.GrouperSession;
import edu.internet2.middleware.subject.Subject;
import edu.internet2.middleware.subject.provider.SubjectTypeEnum;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Initiates a GoogleAppsFullSync from command-line
 *
 * @author John Gasper, Unicon
 */
public class GoogleAppsFullSync {

    private static final Logger LOG = LoggerFactory.getLogger(GoogleAppsFullSync.class);

    /** "Boolean" used to delay change log processing when a full sync is running. */
    private static final HashMap<String, String> fullSyncIsRunning = new HashMap<String, String>();
    private static final Object fullSyncIsRunningLock = new Object();

    private GoogleGrouperConnector connector;
    private String consumerName;
    private GoogleAppsSyncProperties properties;

    public GoogleAppsFullSync(String consumerName) {
        this.consumerName = consumerName;
    }

    public static void main(String[] args) {
        if (args.length == 0) {
            System.console().printf("Google Change Log Consumer Name must be provided\n");
            System.console().printf("*nix: googleAppsFullSync.sh consumerName [--dry-run]\n");
            System.console().printf("Windows: googleAppsFullSync.bat consumerName [--dry-run]\n");

            System.exit(-1);
        }

        try {
            GoogleAppsFullSync googleAppsFullSync = new GoogleAppsFullSync(args[0]);
            googleAppsFullSync.process(args.length > 1 && args[1].equalsIgnoreCase("--dry-run"));

        } catch (Exception e) {
            System.console().printf(e.toString() + ": \n");
            e.printStackTrace();
        }

        System.exit(0);
    }

    public static boolean isFullSyncRunning(String consumerName) {
        synchronized (fullSyncIsRunningLock) {
            return fullSyncIsRunning.get(consumerName) != null
                    && Boolean.valueOf(fullSyncIsRunning.get(consumerName));
        }
    }

    /**
     * Runs a fullSync.
     * @param dryRun indicates that this is dryRun
     */
    public void process(boolean dryRun) {

        synchronized (fullSyncIsRunningLock) {
            fullSyncIsRunning.put(consumerName, Boolean.toString(true));
        }

        connector = new GoogleGrouperConnector();

        //Start with a clean cache
        GoogleCacheManager.googleGroups().clear();
        GoogleCacheManager.googleGroups().clear();

        properties = new GoogleAppsSyncProperties(consumerName);

        Pattern googleGroupFilter = Pattern.compile(properties.getGoogleGroupFilter());

        try {
            connector.initialize(consumerName, properties);

            if (properties.getprefillGoogleCachesForFullSync()) {
                connector.populateGoogleCache();
            }

        } catch (GeneralSecurityException e) {
            LOG.error("Google Apps Consume '{}' Full Sync - This consumer failed to initialize: {}", consumerName,
                    e.getMessage());
        } catch (IOException e) {
            LOG.error("Google Apps Consume '{}' Full Sync - This consumer failed to initialize: {}", consumerName,
                    e.getMessage());
        }

        GrouperSession grouperSession = null;

        try {
            grouperSession = GrouperSession.startRootSession();
            connector.getGoogleSyncAttribute();
            connector.cacheSyncedGroupsAndStems(true);

            // time context processing
            final StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            //Populate a normalized list (google naming) of Grouper groups
            ArrayList<ComparableGroupItem> grouperGroups = new ArrayList<ComparableGroupItem>();
            for (String groupKey : connector.getSyncedGroupsAndStems().keySet()) {
                if (connector.getSyncedGroupsAndStems().get(groupKey).equalsIgnoreCase("yes")) {
                    edu.internet2.middleware.grouper.Group group = connector.fetchGrouperGroup(groupKey);

                    if (group != null) {
                        grouperGroups.add(new ComparableGroupItem(
                                connector.getAddressFormatter().qualifyGroupAddress(group.getName()), group));
                    }
                }
            }

            //Populate a comparable list of Google groups
            ArrayList<ComparableGroupItem> googleGroups = new ArrayList<ComparableGroupItem>();
            for (String groupName : GoogleCacheManager.googleGroups().getKeySet()) {
                if (googleGroupFilter.matcher(groupName.replace("@" + properties.getGoogleDomain(), "")).find()) {
                    googleGroups.add(new ComparableGroupItem(groupName));
                    LOG.debug("Google Apps Consumer '{}' Full Sync - {} group matches group filter: included",
                            consumerName, groupName);
                } else {
                    LOG.debug("Google Apps Consumer '{}' Full Sync - {} group does not match group filter: ignored",
                            consumerName, groupName);
                }
            }

            //Get our sets
            Collection<ComparableGroupItem> extraGroups = CollectionUtils.subtract(googleGroups, grouperGroups);
            processExtraGroups(dryRun, extraGroups);

            Collection<ComparableGroupItem> missingGroups = CollectionUtils.subtract(grouperGroups, googleGroups);
            processMissingGroups(dryRun, missingGroups);

            Collection<ComparableGroupItem> matchedGroups = CollectionUtils.intersection(grouperGroups,
                    googleGroups);
            processMatchedGroups(dryRun, matchedGroups);

            // stop the timer and log
            stopWatch.stop();
            LOG.debug("Google Apps Consumer '{}' Full Sync - Processed, Elapsed time {}",
                    new Object[] { consumerName, stopWatch });

        } finally {
            GrouperSession.stopQuietly(grouperSession);

            synchronized (fullSyncIsRunningLock) {
                fullSyncIsRunning.put(consumerName, Boolean.toString(true));
            }
        }

    }

    private void processMatchedGroups(boolean dryRun, Collection<ComparableGroupItem> matchedGroups) {
        for (ComparableGroupItem item : matchedGroups) {
            LOG.info("Google Apps Consumer '{}' Full Sync - examining matched group: {} ({})",
                    new Object[] { consumerName, item.getGrouperGroup().getName(), item });

            Group gooGroup = null;
            try {
                gooGroup = connector.fetchGooGroup(item.getName());
            } catch (IOException e) {
                LOG.error("Google Apps Consume '{}' Full Sync - Error fetching matched group ({}): {}",
                        new Object[] { consumerName, item.getName(), e.getMessage() });
            }
            boolean updated = false;

            if (gooGroup == null) {
                LOG.error(
                        "Google Apps Consume '{}' Full Sync - Error fetching matched group ({}); it disappeared during processing.",
                        new Object[] { consumerName, item.getName() });
            } else {

                if (!item.getGrouperGroup().getDescription().equalsIgnoreCase(gooGroup.getDescription())) {
                    if (!dryRun) {
                        gooGroup.setDescription(item.getGrouperGroup().getDescription());
                        updated = true;
                    }
                }

                if (!item.getGrouperGroup().getDisplayExtension().equalsIgnoreCase(gooGroup.getName())) {
                    if (!dryRun) {
                        gooGroup.setName(item.getGrouperGroup().getDisplayExtension());
                        updated = true;
                    }
                }

                if (updated) {
                    try {
                        connector.updateGooGroup(item.getName(), gooGroup);
                    } catch (IOException e) {
                        LOG.error("Google Apps Consume '{}' Full Sync - Error updating matched group ({}): {}",
                                new Object[] { consumerName, item.getName(), e.getMessage() });
                    }
                }

                //Retrieve Membership
                ArrayList<ComparableMemberItem> grouperMembers = new ArrayList<ComparableMemberItem>();
                for (edu.internet2.middleware.grouper.Member member : item.getGrouperGroup().getMembers()) {
                    if (member.getSubjectType() == SubjectTypeEnum.PERSON) {
                        grouperMembers.add(new ComparableMemberItem(
                                connector.getAddressFormatter().qualifySubjectAddress(member.getSubjectId()),
                                member));
                    }
                }

                ArrayList<ComparableMemberItem> googleMembers = new ArrayList<ComparableMemberItem>();
                List<Member> memberList = null;

                try {
                    memberList = connector.getGooMembership(item.getName());
                } catch (IOException e) {
                    LOG.error(
                            "Google Apps Consume '{}' Full Sync - Error fetching membership list for group({}): {}",
                            new Object[] { consumerName, item.getName(), e.getMessage() });
                }

                if (memberList == null) {
                    LOG.error(
                            "Google Apps Consume '{}' Full Sync - Error fetching membership list for group ({}); it's null",
                            new Object[] { consumerName, item.getName() });

                } else {
                    for (Member member : memberList) {
                        googleMembers.add(new ComparableMemberItem(member.getEmail()));
                    }

                    Collection<ComparableMemberItem> extraMembers = CollectionUtils.subtract(googleMembers,
                            grouperMembers);
                    if (!properties.shouldIgnoreExtraGoogleMembers()) {
                        processExtraGroupMembers(item, extraMembers, dryRun);
                    }

                    Collection<ComparableMemberItem> missingMembers = CollectionUtils.subtract(grouperMembers,
                            googleMembers);
                    processMissingGroupMembers(item, missingMembers, gooGroup, dryRun);

                    Collection<ComparableMemberItem> matchedMembers = CollectionUtils.intersection(grouperMembers,
                            googleMembers);
                    processMatchedGroupMembers(item, matchedMembers, dryRun);
                }
            }
        }
    }

    private void processMatchedGroupMembers(ComparableGroupItem group,
            Collection<ComparableMemberItem> matchedMembers, boolean dryRun) {
        for (ComparableMemberItem member : matchedMembers) {
            if (!dryRun) {
                edu.internet2.middleware.grouper.Member grouperMember = member.getGrouperMember();

                try {
                    connector.updateGooMember(group.getGrouperGroup(), grouperMember.getSubject(),
                            connector.determineRole(grouperMember, group.getGrouperGroup()));
                } catch (IOException e) {
                    LOG.error(
                            "Google Apps Consume '{}' Full Sync - Error updating existing user ({}) from existing group ({}): {}",
                            new Object[] { consumerName, member.getEmail(), group.getName(), e.getMessage() });
                }
            }
        }
    }

    private void processMissingGroupMembers(ComparableGroupItem group,
            Collection<ComparableMemberItem> missingMembers, Group gooGroup, boolean dryRun) {
        for (ComparableMemberItem member : missingMembers) {
            LOG.info(
                    "Google Apps Consume '{}' Full Sync - Creating missing user/member ({}) from extra group ({}).",
                    new Object[] { consumerName, member.getEmail(), group.getName() });
            if (!dryRun) {
                Subject subject = connector.fetchGrouperSubject(member.getGrouperMember().getSubjectSourceId(),
                        member.getGrouperMember().getSubjectId());
                User user = connector.fetchGooUser(member.getEmail());

                if (user == null) {
                    try {
                        user = connector.createGooUser(subject);
                    } catch (IOException e) {
                        LOG.error(
                                "Google Apps Consume '{}' Full Sync - Error creating missing user ({}) from extra group ({}): {}",
                                new Object[] { consumerName, member.getEmail(), group.getName(), e.getMessage() });
                    }
                }

                if (user != null) {
                    try {
                        connector.createGooMember(gooGroup, user,
                                connector.determineRole(member.getGrouperMember(), group.getGrouperGroup()));
                    } catch (IOException e) {
                        LOG.error(
                                "Google Apps Consume '{}' Full Sync - Error creating missing member ({}) from extra group ({}): {}",
                                new Object[] { consumerName, member.getEmail(), group.getName(), e.getMessage() });
                    }
                }
            }
        }
    }

    private void processExtraGroupMembers(ComparableGroupItem group, Collection<ComparableMemberItem> extraMembers,
            boolean dryRun) {
        for (ComparableMemberItem member : extraMembers) {
            LOG.info("Google Apps Consume '{}' Full Sync - Removing extra member ({}) from matched group ({})",
                    new Object[] { consumerName, member.getEmail(), group.getName() });
            if (!dryRun) {
                try {
                    connector.removeGooMembership(group.getName(), member.getGrouperMember().getSubject());
                } catch (IOException e) {
                    LOG.warn("Google Apps Consume '{}' - Error removing membership ({}) from Google Group ({}): {}",
                            new Object[] { consumerName, member.getEmail(), group.getName(), e.getMessage() });
                }
            }
        }
    }

    private void processMissingGroups(boolean dryRun, Collection<ComparableGroupItem> missingGroups) {
        for (ComparableGroupItem item : missingGroups) {
            LOG.info("Google Apps Consumer '{}' Full Sync - adding missing Google group: {} ({})",
                    new Object[] { consumerName, item.getGrouperGroup().getName(), item });

            if (!dryRun) {
                try {
                    connector.createGooGroupIfNecessary(item.getGrouperGroup());
                } catch (IOException e) {
                    LOG.error("Google Apps Consume '{}' Full Sync - Error adding missing group ({}): {}",
                            new Object[] { consumerName, item.getName(), e.getMessage() });
                }
            }
        }
    }

    private void processExtraGroups(boolean dryRun, Collection<ComparableGroupItem> extraGroups) {
        for (ComparableGroupItem item : extraGroups) {
            LOG.info("Google Apps Consumer '{}' Full Sync - removing extra Google group: {}", consumerName, item);

            if (!dryRun) {
                try {
                    connector.deleteGooGroupByEmail(item.getName());
                } catch (IOException e) {
                    LOG.error("Google Apps Consume '{}' Full Sync - Error removing extra group ({}): {}",
                            new Object[] { consumerName, item.getName(), e.getMessage() });
                }
            }
        }
    }

}