org.fao.geonet.MergeUsersByUsernameDatabaseMigration.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.MergeUsersByUsernameDatabaseMigration.java

Source

/*
 * Copyright (C) 2001-2018 Food and Agriculture Organization of the
 * United Nations (FAO-UN), United Nations World Food Programme (WFP)
 * and United Nations Environment Programme (UNEP)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
 * Rome - Italy. email: geonetwork@osgeo.org
 */
package org.fao.geonet;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.fao.geonet.domain.*;
import org.fao.geonet.exceptions.TaskExecutionException;
import org.fao.geonet.kernel.DataManager;
import org.fao.geonet.repository.*;
import org.fao.geonet.repository.specification.MetadataSpecs;
import org.fao.geonet.repository.specification.MetadataStatusSpecs;
import org.fao.geonet.repository.specification.UserGroupSpecs;
import org.fao.geonet.utils.Log;
import org.springframework.context.ApplicationContext;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.transaction.Transactional;
import java.util.*;

/**
 * This migration merge users sharing the same case insensitive user name. Final result will be only one user with
 * lower case username.
 */
public class MergeUsersByUsernameDatabaseMigration implements ContextAwareTask {

    @Transactional
    @Override
    public void run(ApplicationContext applicationContext) throws TaskExecutionException {
        UserRepository userRepository = applicationContext.getBean(UserRepository.class);
        List<String> duplicatedUsernamesList = userRepository.findDuplicatedUsernamesCaseInsensitive();
        Log.debug(Log.JEEVES, "Found these duplicated usernames: " + duplicatedUsernamesList);
        try {
            for (String duplicatedUsername : duplicatedUsernamesList) {
                mergeUsers(applicationContext, duplicatedUsername);
            }
        } catch (Exception e) {
            Log.error(Log.JEEVES, "Exception merging users", e);
            throw new TaskExecutionException(e);
        }

    }

    private void mergeUsers(ApplicationContext applicationContext, String duplicatedUsername) throws Exception {
        UserRepository userRepository = applicationContext.getBean(UserRepository.class);
        List<User> duplicatedUserList = userRepository.findByUsernameIgnoreCase(duplicatedUsername);

        duplicatedUserList.sort(Comparator.comparing(User::getProfile));
        User greatestProfileUser = duplicatedUserList.get(0);
        User userToKeep = userRepository.findOne(greatestProfileUser.getId());
        Set<String> emails = new HashSet<>();
        Set<Address> addresses = new HashSet<>();

        mergeGroups(applicationContext, duplicatedUserList, userToKeep);
        transferMetadata(applicationContext, duplicatedUserList, userToKeep);
        transferSavedSelections(applicationContext, duplicatedUserList, userToKeep);

        User tempUser = new User();
        for (int i = duplicatedUserList.size() - 1; i >= 0; i--) { // i = 1  is intended
            User duplicatedUser = duplicatedUserList.get(i);
            addresses.addAll(duplicatedUser.getAddresses());
            mergeUser(tempUser, duplicatedUser);
            emails.addAll(duplicatedUser.getEmailAddresses());
        }

        mergeUser(userToKeep, tempUser);
        duplicatedUserList.remove(greatestProfileUser);
        userRepository.delete(duplicatedUserList);
        userToKeep.setUsername(userToKeep.getUsername().toLowerCase());
        userRepository.save(userToKeep);
    }

    private void transferSavedSelections(ApplicationContext applicationContext, List<User> duplicatedUserList,
            User userToKeep) {
        UserSavedSelectionRepository userSavedSelectionRepository = applicationContext
                .getBean(UserSavedSelectionRepository.class);
        // TODO

        for (int i = 1; i < duplicatedUserList.size(); i++) { // i intentionally initialised to 1
            userSavedSelectionRepository.deleteAllByUser(duplicatedUserList.get(i).getId());
        }
    }

    /**
     * Transfer the metadata ownership from all the users in duplicatedUserList to userToKeep
     *
     * @param applicationContext   the application context bean factory
     * @param oldMetadataOwnerList list with the users whose metadata is going to be transferred.
     * @param newMetadataOwner     new metadata owner user.
     * @throws Exception if anything doesn't work.
     */
    void transferMetadata(ApplicationContext applicationContext, List<User> oldMetadataOwnerList,
            User newMetadataOwner) throws Exception {
        MetadataRepository metadataRepository = applicationContext.getBean(MetadataRepository.class);
        DataManager dataManager = applicationContext.getBean(DataManager.class);
        MetadataStatusRepository metadataStatusRepository = applicationContext
                .getBean(MetadataStatusRepository.class);

        for (int i = 1; i < oldMetadataOwnerList.size(); i++) {
            User oldOwner = oldMetadataOwnerList.get(i);

            // Transfer metadata to user but keep old group
            List<Metadata> metadataList = metadataRepository.findAll(MetadataSpecs.isOwnedByUser(oldOwner.getId()));
            for (Metadata metadata : metadataList) {
                dataManager.updateMetadataOwner(metadata.getId(), Integer.toString(newMetadataOwner.getId()),
                        Integer.toString(metadata.getSourceInfo().getGroupOwner()));
            }

            // Transfer metadata status
            List<MetadataStatus> metadataStatusList = metadataStatusRepository
                    .findAll(MetadataStatusSpecs.hasUserId(oldOwner.getId()));
            for (MetadataStatus metadataStatus : metadataStatusList) {
                MetadataStatusId metadataStatusId = metadataStatus.getId();

                MetadataStatusId newMetadataStatusId = new MetadataStatusId();
                newMetadataStatusId.setUserId(newMetadataOwner.getId());
                newMetadataStatusId.setMetadataId(metadataStatusId.getMetadataId());
                newMetadataStatusId.setChangeDate(metadataStatusId.getChangeDate());
                metadataStatusId.setStatusId(metadataStatusId.getStatusId());

                MetadataStatus newMetadataStatus = new MetadataStatus();
                newMetadataStatus.setId(newMetadataStatusId);
                newMetadataStatus.setChangeMessage(metadataStatus.getChangeMessage());
                newMetadataStatus.setStatusValue(metadataStatus.getStatusValue());
                metadataStatusRepository.save(newMetadataStatus);
            }
            metadataStatusRepository.deleteAllById_UserId(oldOwner.getId());
            metadataStatusRepository.save(metadataStatusList);

            dataManager.indexMetadata(Lists.transform(metadataList, new Function<Metadata, String>() {
                @Nonnull
                @Override
                public String apply(@Nullable Metadata input) {
                    return String.valueOf(input.getId());
                }
            }));
        }
    }

    /**
     * Add the userToKeep user to all the groups of duplicateduserList with the same profile these have in each group.
     *
     * @param applicationContext the application context bean factory.
     * @param duplicatedUserList list of users used to get the group list.
     * @param userToKeep         the user to add to the groups of duplicatedUserList.
     */
    void mergeGroups(ApplicationContext applicationContext, List<User> duplicatedUserList, User userToKeep) {
        GroupRepository groupRepository = applicationContext.getBean(GroupRepository.class);
        UserGroupRepository userGroupRepository = applicationContext.getBean(UserGroupRepository.class);
        Set<String> listOfAddedProfiles = new HashSet<>();
        List<UserGroup> mergedUserGroups = new ArrayList<>();
        List<Integer> userIdList = new ArrayList<>(duplicatedUserList.size());

        for (int i = 0; i < duplicatedUserList.size(); i++) {
            User userToProcess = duplicatedUserList.get(i);
            userIdList.add(userToProcess.getId());

            List<UserGroup> userGroupsToProcess = userGroupRepository
                    .findAll(UserGroupSpecs.hasUserId(userToProcess.getId()));
            if (Log.isDebugEnabled(Log.JEEVES)) {
                Log.debug(Log.JEEVES, "Groups of user {" + userToProcess.getId() + " - "
                        + userToProcess.getUsername() + "}: " + userGroupsToProcess.size());
                Log.debug(Log.JEEVES, userGroupsToProcess);
            }

            for (UserGroup ug : userGroupsToProcess) {
                String key = ug.getProfile().name() + "#" + ug.getGroup().getId();
                if (!listOfAddedProfiles.contains(key)) {
                    listOfAddedProfiles.add(key);
                }
            }
        }
        // Create a new UserGroup for each one of the old profiles
        for (String key : listOfAddedProfiles) {
            String[] splitKey = StringUtils.split(key, "#");
            String profileName = splitKey[0];
            Integer groupId = Integer.valueOf(splitKey[1]);

            Group group = groupRepository.findOne(groupId);
            Profile profile = Profile.findProfileIgnoreCase(profileName);
            UserGroup userGroup = new UserGroup().setGroup(group).setProfile(profile).setUser(userToKeep);
            mergedUserGroups.add(userGroup);
        }

        userGroupRepository.deleteAllByIdAttribute(UserGroupId_.userId, userIdList);
        userGroupRepository.save(mergedUserGroups);
    }

    public User mergeUser(User toOverwrite, User toKeep) {
        if (StringUtils.isNotBlank(toKeep.getSurname())) {
            toOverwrite.setSurname(toKeep.getSurname());
        }
        if (StringUtils.isNotBlank(toKeep.getName())) {
            toOverwrite.setName(toKeep.getName());
        }
        if (StringUtils.isNotBlank(toKeep.getOrganisation())) {
            toOverwrite.setOrganisation(toKeep.getOrganisation());
        }
        if (StringUtils.isNotBlank(toKeep.getKind())) {
            toOverwrite.setKind(toKeep.getKind());
        }
        if (StringUtils.isNotBlank(toKeep.getProfile().name())) {
            toOverwrite.setProfile(toKeep.getProfile());
        }

        if (!toKeep.getEmailAddresses().isEmpty()) {
            toOverwrite.getEmailAddresses().clear();
            toOverwrite.getEmailAddresses().addAll(toKeep.getEmailAddresses());
        }

        ArrayList<Address> otherAddresses = new ArrayList<Address>(toKeep.getAddresses());
        if (!otherAddresses.isEmpty()) {
            for (Iterator<Address> iterator = toOverwrite.getAddresses().iterator(); iterator.hasNext();) {
                Address address = iterator.next();
                boolean found = false;

                for (Iterator<Address> iterator2 = otherAddresses.iterator(); iterator2.hasNext();) {
                    Address otherAddress = iterator2.next();
                    if (otherAddress.getId() == address.getId()) {
                        address.mergeAddress(otherAddress, false);
                        found = true;
                        iterator2.remove();
                        break;
                    }
                }
            }
            toOverwrite.getAddresses().addAll(otherAddresses);
        }

        toOverwrite.getSecurity().mergeSecurity(toKeep.getSecurity(), false);
        return toOverwrite;
    }
}