com.amazonaws.eclipse.core.accounts.AccountInfoProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.eclipse.core.accounts.AccountInfoProvider.java

Source

/*
 * Copyright 2010-2014 Amazon Technologies, Inc.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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 com.amazonaws.eclipse.core.accounts;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.profile.ProfilesConfigFile;
import com.amazonaws.auth.profile.ProfilesConfigFileWriter;
import com.amazonaws.auth.profile.internal.Profile;
import com.amazonaws.eclipse.core.AccountInfo;
import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.core.accounts.preferences.PluginPreferenceStoreAccountCredentialsConfiguration;
import com.amazonaws.eclipse.core.accounts.preferences.PluginPreferenceStoreAccountOptionalConfiguration;
import com.amazonaws.eclipse.core.accounts.profiles.SdkProfilesCredentialsConfiguration;
import com.amazonaws.eclipse.core.preferences.PreferenceConstants;
import com.amazonaws.eclipse.core.ui.preferences.AwsAccountPreferencePage;
import com.amazonaws.util.StringUtils;

/**
 * A class that loads and returns all the configured AccountInfo. It's
 * also responsible for hooking the AccountInfoChangeListener.
 */
public class AccountInfoProvider {

    private final IPreferenceStore prefStore;

    /**
     * Loading from the credentials file is an expensive operation, so we want
     * to cache all the loaded AccountInfo objects.
     */
    Map<String, AccountInfo> profileAccountInfoCache = new LinkedHashMap<String, AccountInfo>();

    /** All the registered AccountInfoChangeListener */
    List<AccountInfoChangeListener> listeners = new LinkedList<AccountInfoChangeListener>();

    /**
     * Initialize the provider with the given preference store instance.
     */
    public AccountInfoProvider(IPreferenceStore prefStore) {
        this.prefStore = prefStore;
    }

    /**
     * Returns all the account info that are loaded from the credential profiles
     * file. For performance reason, this method won't attempt to reload the
     * accounts upon each method call, therefore the returned result might be
     * out of sync with the external source. Call the {@link #refresh()} method
     * to forcefully reload all the account info.
     *
     * @return Map from the accountId to each of the profile-based AccountInfo
     *         object.
     */
    public Map<String, AccountInfo> getAllProfileAccountInfo() {
        return Collections.unmodifiableMap(profileAccountInfoCache);
    }

    /**
     * Returns the profile account info by the account identifier.
     *
     * @see #getAllProfileAccountInfo()
     */
    public AccountInfo getProfileAccountInfo(String accountId) {
        return profileAccountInfoCache.get(accountId);
    }

    /**
     * Loads and returns all the legacy account configuration from the
     * preference store system. The identifiers of legacy accounts could be
     * found in the following preference keys:
     * (1) accountIds              (ids of all the global accounts)
     * (2) accountIds-region       (e.g. "accountIds-cn-north-1", ids of all the regional accounts)
     *
     * @return Map from the accountId to each of the legacy AccountInfo
     *         object.
     */
    public Map<String, AccountInfo> getAllLegacyPreferenceStoreAccontInfo() {
        Map<String, AccountInfo> preferenceStoreAccounts = new LinkedHashMap<String, AccountInfo>();

        // Global accounts
        preferenceStoreAccounts.putAll(loadPreferenceStoreAccountsByRegion(null));

        // Regional accounts
        List<String> regionsWithDefaultAccount = AwsAccountPreferencePage.getRegionsWithDefaultAccounts(prefStore);
        for (String regionId : regionsWithDefaultAccount) {
            preferenceStoreAccounts.putAll(loadPreferenceStoreAccountsByRegion(regionId));
        }

        return preferenceStoreAccounts;
    }

    /**
     * Returns the legacy preference store account info by the account
     * identifier.
     *
     * @see #getAllLegacyPreferenceStoreAccontInfo()
     */
    public AccountInfo getLegacyPreferenceStoreAccountInfo(String accountId) {
        return getAllLegacyPreferenceStoreAccontInfo().get(accountId);
    }

    /**
     * Forces the provider to refresh all the profile AccountInfo it vends.
     *
     * @param boostrapCredentialsFile
     *            If set true, this method will create a credentials file with a
     *            default profile if no account is currently configured in the
     *            toolkit.
     * @param showWarningOnFailure
     *            If true, this method will show a warning message box if it
     *            fails to load accounts.
     */
    public synchronized void refreshProfileAccountInfo(final boolean boostrapCredentialsFile,
            final boolean showWarningOnFailure) {
        reloadProfileAccountInfo(boostrapCredentialsFile, showWarningOnFailure);

        // Notify the change listeners
        for (AccountInfoChangeListener listener : listeners) {
            listener.onAccountInfoChange();
        }
    }

    /**
     * Update the following preference value:
     *  (1) P_CREDENTIAL_PROFILE_ACCOUNT_IDS - ids of all the accounts that were loaded from credential profiles file.
     *  (2) accountId:P_CREDENTIAL_PROFILE_NAME - name of the credential profile
     *
     * This method needs to be called whenever we reload profile credentials.
     */
    public void updateProfileAccountMetadataInPreferenceStore(Collection<AccountInfo> accounts) {
        List<String> accountIds = new LinkedList<String>();
        for (AccountInfo accountInfo : accounts) {
            accountIds.add(accountInfo.getInternalAccountId());
        }

        String accountIdsString = StringUtils.join(PreferenceConstants.ACCOUNT_ID_SEPARATOR,
                accountIds.toArray(new String[accountIds.size()]));
        prefStore.setValue(PreferenceConstants.P_CREDENTIAL_PROFILE_ACCOUNT_IDS, accountIdsString);

        for (AccountInfo profileAccount : accounts) {
            String profileNamePrefKey = profileAccount.getInternalAccountId() + ":"
                    + PreferenceConstants.P_CREDENTIAL_PROFILE_NAME;
            prefStore.setValue(profileNamePrefKey, profileAccount.getAccountName());
        }
    }

    /**
     * Register an AccountInfoChangeListener to this provider. All the
     * registered listeners will be notified whenever this provider is
     * refreshed.
     *
     * @param listener
     *            The new AccountInfoChangeListener to be registered.
     */
    public void addAccountInfoChangeListener(AccountInfoChangeListener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }
    }

    /**
     * Remove an AccountInfoChangeListener from this provider.
     */
    public void removeAccountInfoChangeListener(AccountInfoChangeListener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }
    }

    /* Profile-based account info */

    private void reloadProfileAccountInfo(final boolean boostrapCredentialsFile,
            final boolean showWarningOnFailure) {

        profileAccountInfoCache.clear();

        /* Load the credential profiles file */

        String credFileLocation = prefStore.getString(PreferenceConstants.P_CREDENTIAL_PROFILE_FILE_LOCATION);
        File credFile = new File(credFileLocation);

        ProfilesConfigFile profileConfigFile = null;

        try {
            if (!credFile.exists() && boostrapCredentialsFile) {
                ProfilesConfigFileWriter.dumpToFile(credFile, true, // overwrite=true
                        new Profile(PreferenceConstants.DEFAULT_ACCOUNT_NAME, new BasicAWSCredentials("", "")));
            }

            profileConfigFile = new ProfilesConfigFile(credFile);
        } catch (Exception e) {
            String errorMsg = String.format("Failed to load credential profiles from (%s).", credFileLocation);
            AwsToolkitCore.getDefault().logInfo(errorMsg + e.getMessage());

            if (showWarningOnFailure) {
                MessageDialog.openError(null, "Unable to load profile accounts",
                        errorMsg + " Please check that your credentials file is at the correct location "
                                + "and that it is in the correct format.");
            }
        }

        if (profileConfigFile == null)
            return;

        /* Set up the AccountInfo objects */

        // Map from profile name to its pre-configured account id
        Map<String, String> exisitingProfileAccountIds = getExistingProfileAccountIds();

        // Iterate through the newly loaded profiles. Re-use the existing
        // account id if the profile name is already configured in the toolkit.
        // Otherwise assign a new UUID for it.
        for (Entry<String, Profile> entry : profileConfigFile.getAllProfiles().entrySet()) {
            String profileName = entry.getKey();
            Profile profile = entry.getValue();

            String accountId = exisitingProfileAccountIds.get(profileName);
            if (accountId == null) {
                AwsToolkitCore.getDefault().logInfo("No profile found: " + profileName);
                accountId = UUID.randomUUID().toString();
            }

            AccountInfo profileAccountInfo = new AccountInfoImpl(accountId,
                    new SdkProfilesCredentialsConfiguration(prefStore, accountId, profile),
                    // Profile accounts use profileName as the preference name prefix
                    // @see PluginPreferenceStoreAccountOptionalConfiguration
                    new PluginPreferenceStoreAccountOptionalConfiguration(prefStore, profileName));
            profileAccountInfoCache.put(accountId, profileAccountInfo);
        }

        /* Update the preference store metadata for the newly loaded profile accounts */

        updateProfileAccountMetadataInPreferenceStore(profileAccountInfoCache.values());
    }

    /**
     * The toolkit uses the "credentialProfileAccountIds" preference property to
     * store the identifiers of all the profile-based accounts. This method
     * checks this preference value and returns a map of all the existing
     * profile accounts that are already configured in the toolkit.
     *
     * @return Key - profile name;  Value - account id
     */
    private Map<String, String> getExistingProfileAccountIds() {
        Map<String, String> exisitingProfileAccounts = new HashMap<String, String>();

        // Ids of all the profile accounts currently configured in the toolkit
        String[] profileAccountIds = prefStore.getString(PreferenceConstants.P_CREDENTIAL_PROFILE_ACCOUNT_IDS)
                .split(PreferenceConstants.ACCOUNT_ID_SEPARATOR_REGEX);

        for (String accountId : profileAccountIds) {
            String configuredProfileName = prefStore
                    .getString(accountId + ":" + PreferenceConstants.P_CREDENTIAL_PROFILE_NAME);
            if (configuredProfileName != null && !configuredProfileName.isEmpty()) {
                exisitingProfileAccounts.put(configuredProfileName, accountId);
            }
        }

        return exisitingProfileAccounts;
    }

    /* Pref-store-based account info */

    /**
     * Returns a map of all the preference-store-based account info.
     *
     * @param region
     *            Null value indicates the global region
     */
    @SuppressWarnings("deprecation")
    private Map<String, AccountInfo> loadPreferenceStoreAccountsByRegion(String regionId) {
        String p_regionalAccountIds = regionId == null ? PreferenceConstants.P_ACCOUNT_IDS
                : PreferenceConstants.P_ACCOUNT_IDS + "-" + regionId;

        String[] accountIds = prefStore.getString(p_regionalAccountIds)
                .split(PreferenceConstants.ACCOUNT_ID_SEPARATOR_REGEX);

        Map<String, AccountInfo> accounts = new LinkedHashMap<String, AccountInfo>();

        for (String accountId : accountIds) {
            if (accountId.length() > 0) {
                // Both credential and optional configurations are preference-store-based
                AccountInfo prefStoreAccountInfo = new AccountInfoImpl(accountId,
                        new PluginPreferenceStoreAccountCredentialsConfiguration(prefStore, accountId),
                        new PluginPreferenceStoreAccountOptionalConfiguration(prefStore, accountId));

                accounts.put(accountId, prefStoreAccountInfo);
            }
        }

        return accounts;
    }
}