| /******************************************************************************* |
| * Copyright (c) 2013, 2015 Ericsson AB and others. All rights reserved. This |
| * program and the accompanying materials are made available under the terms of |
| * the Eclipse Public License v1.0 which accompanies this distribution, and is |
| * available at http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Ericsson AB - initial API and implementation |
| * Ericsson AB (Pascal Rapicault) |
| * Ericsson AB (Hamdan Msheik) |
| * Red Hat Inc. - Bug 460967 |
| ******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.ui.sdk.scheduler.migration; |
| |
| import java.io.File; |
| import java.net.URI; |
| import java.util.*; |
| import org.eclipse.core.runtime.URIUtil; |
| import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; |
| import org.eclipse.equinox.internal.p2.engine.EngineActivator; |
| import org.eclipse.equinox.internal.p2.metadata.query.UpdateQuery; |
| import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.*; |
| import org.eclipse.equinox.p2.core.IAgentLocation; |
| import org.eclipse.equinox.p2.core.IProvisioningAgent; |
| import org.eclipse.equinox.p2.engine.IProfile; |
| import org.eclipse.equinox.p2.engine.IProfileRegistry; |
| import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery; |
| import org.eclipse.equinox.p2.engine.query.UserVisibleRootQuery; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnit; |
| import org.eclipse.equinox.p2.query.IQueryResult; |
| import org.eclipse.equinox.p2.query.QueryUtil; |
| import org.eclipse.equinox.p2.repository.IRepositoryManager; |
| import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; |
| import org.eclipse.equinox.p2.ui.ProvisioningUI; |
| import org.eclipse.jface.wizard.WizardDialog; |
| import org.eclipse.osgi.service.datalocation.Location; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| |
| public class MigrationSupport { |
| private static final String ECLIPSE_P2_SKIP_MIGRATION_WIZARD = "eclipse.p2.skipMigrationWizard"; //$NON-NLS-1$ |
| private static final String ECLIPSE_P2_SKIP_MOVED_INSTALL_DETECTION = "eclipse.p2.skipMovedInstallDetection"; //$NON-NLS-1$ |
| |
| //The return value indicates if the migration dialog has been shown or not. It does not indicate whether the migration has completed. |
| public boolean performMigration(IProvisioningAgent agent, IProfileRegistry registry, IProfile currentProfile) { |
| boolean skipWizard = Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(ECLIPSE_P2_SKIP_MIGRATION_WIZARD)); |
| if (skipWizard) |
| return false; |
| |
| IProfile previousProfile = null; |
| URI[] reposToMigrate = null; |
| if (!skipFirstTimeMigration() && !configurationSpecifiedManually() && isFirstTimeRunningThisSharedInstance(agent, registry, currentProfile)) { |
| File searchRoot = getSearchLocation(); |
| if (searchRoot == null) |
| return false; |
| |
| IProvisioningAgent otherConfigAgent = new PreviousConfigurationFinder(getConfigurationLocation().getParentFile()).findPreviousInstalls(searchRoot, getInstallFolder()); |
| if (otherConfigAgent == null) { |
| return false; |
| } |
| previousProfile = ((IProfileRegistry) otherConfigAgent.getService(IProfileRegistry.SERVICE_NAME)).getProfile(IProfileRegistry.SELF); |
| if (previousProfile == null) |
| return false; |
| |
| reposToMigrate = ((IMetadataRepositoryManager) otherConfigAgent.getService(IMetadataRepositoryManager.SERVICE_NAME)).getKnownRepositories(IRepositoryManager.REPOSITORIES_NON_SYSTEM); |
| reposToMigrate = copyOf(reposToMigrate, reposToMigrate.length + 1); |
| reposToMigrate[reposToMigrate.length - 1] = getURIForProfile(otherConfigAgent, previousProfile); |
| } |
| |
| if (previousProfile == null && baseChangedSinceLastPresentationOfWizard(agent, registry, currentProfile)) |
| previousProfile = findMostRecentReset(registry, currentProfile); |
| |
| if (previousProfile == null) |
| return false; |
| |
| Collection<IInstallableUnit> unitsToMigrate = findUnitstoMigrate(previousProfile, currentProfile); |
| if (!unitsToMigrate.isEmpty()) { |
| openMigrationWizard(previousProfile, unitsToMigrate, reposToMigrate); |
| } else { |
| //There is nothing to migrate, so we mark the migration complete |
| rememberMigrationCompleted(); |
| } |
| return true; |
| } |
| |
| private static URI[] copyOf(URI[] original, int newLength) { |
| URI[] copy = new URI[newLength]; |
| int copyCount = Math.min(original.length, newLength); |
| System.arraycopy(original, 0, copy, 0, copyCount); |
| return copy; |
| } |
| |
| private URI getURIForProfile(IProvisioningAgent agent, IProfile profile) { |
| IAgentLocation agentLocation = (IAgentLocation) agent.getService(IAgentLocation.SERVICE_NAME); |
| return URIUtil.append(agentLocation.getRootLocation(), "org.eclipse.equinox.p2.engine/profileRegistry/" + profile.getProfileId() + ".profile"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private File getInstallFolder() { |
| Location configurationLocation = ServiceHelper.getService(EngineActivator.getContext(), Location.class, Location.INSTALL_FILTER); |
| return new File(configurationLocation.getURL().getPath()); |
| } |
| |
| //The search location is two level up from the configuration location. |
| private File getSearchLocation() { |
| File parent = getConfigurationLocation().getParentFile(); |
| if (parent == null) |
| return null; |
| return parent.getParentFile(); |
| } |
| |
| private File getConfigurationLocation() { |
| Location configurationLocation = ServiceHelper.getService(EngineActivator.getContext(), Location.class, Location.CONFIGURATION_FILTER); |
| File configurationFolder = new File(configurationLocation.getURL().getPath()); |
| return configurationFolder; |
| } |
| |
| //Check if the user has explicitly specified -configuration on the command line |
| private boolean configurationSpecifiedManually() { |
| String commandLine = System.getProperty("eclipse.commands"); //$NON-NLS-1$ |
| if (commandLine == null) |
| return false; |
| return commandLine.contains("-configuration\n"); //$NON-NLS-1$ |
| } |
| |
| private boolean skipFirstTimeMigration() { |
| return Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty(ECLIPSE_P2_SKIP_MOVED_INSTALL_DETECTION)); |
| } |
| |
| private boolean isFirstTimeRunningThisSharedInstance(IProvisioningAgent agent, IProfileRegistry registry, IProfile currentProfile) { |
| long[] history = registry.listProfileTimestamps(currentProfile.getProfileId()); |
| boolean isInitial = IProfile.STATE_SHARED_INSTALL_VALUE_INITIAL.equals(registry.getProfileStateProperties(currentProfile.getProfileId(), history[0]).get(IProfile.STATE_PROP_SHARED_INSTALL)); |
| if (isInitial) { |
| if (getLastMigration() >= history[0]) |
| return false; |
| //This detect the case where the user has not done any migration. |
| Map<String, String> sharedRelatedValues = registry.getProfileStateProperties(currentProfile.getProfileId(), IProfile.STATE_PROP_SHARED_INSTALL); |
| if (sharedRelatedValues.containsValue(IProfile.STATE_SHARED_INSTALL_VALUE_NEW)) |
| return false; |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * @param previousProfile is the profile used previous to the current one |
| * @param currentProfile is the current profile used by eclipse. |
| * @return true if set difference between previousProfile units and currentProfile units not empty, otherwise false |
| */ |
| protected Collection<IInstallableUnit> findUnitstoMigrate(IProfile previousProfile, IProfile currentProfile) { |
| //First, try the case of inclusion |
| Set<IInstallableUnit> previousProfileUnits = getUserRoots(previousProfile); |
| Set<IInstallableUnit> currentProfileUnits = currentProfile.available(new UserVisibleRootQuery(), null).toSet(); |
| previousProfileUnits.removeAll(currentProfileUnits); |
| |
| //For the IUs left in the previous profile, look for those that could be in the base but not as roots |
| Iterator<IInstallableUnit> previousProfileIterator = previousProfileUnits.iterator(); |
| while (previousProfileIterator.hasNext()) { |
| if (!currentProfile.available(QueryUtil.createIUQuery(previousProfileIterator.next()), null).isEmpty()) |
| previousProfileIterator.remove(); |
| } |
| |
| //For the IUs left in the previous profile, look for those that could be available in the root but as higher versions (they could be root or not) |
| previousProfileIterator = previousProfileUnits.iterator(); |
| while (previousProfileIterator.hasNext()) { |
| if (!currentProfile.available(new UpdateQuery(previousProfileIterator.next()), null).isEmpty()) |
| previousProfileIterator.remove(); |
| } |
| |
| return previousProfileUnits; |
| } |
| |
| private Set<IInstallableUnit> getUserRoots(IProfile previousProfile) { |
| IQueryResult<IInstallableUnit> allRoots = previousProfile.query(new UserVisibleRootQuery(), null); |
| Set<IInstallableUnit> rootsFromTheBase = previousProfile.query(new IUProfilePropertyQuery("org.eclipse.equinox.p2.base", "true"), null).toUnmodifiableSet(); |
| Set<IInstallableUnit> userRoots = allRoots.toSet(); |
| userRoots.removeAll(rootsFromTheBase); |
| return userRoots; |
| } |
| |
| protected void openMigrationWizard(final IProfile inputProfile, final Collection<IInstallableUnit> unitsToMigrate, final URI[] reposToMigrate) { |
| Display d = Display.getDefault(); |
| d.asyncExec(new Runnable() { |
| public void run() { |
| WizardDialog migrateWizard = new MigrationWizardDialog(getWorkbenchWindowShell(), new MigrationWizard(inputProfile, unitsToMigrate, reposToMigrate, reposToMigrate != null)); |
| migrateWizard.create(); |
| migrateWizard.open(); |
| } |
| }); |
| } |
| |
| private boolean baseChangedSinceLastPresentationOfWizard(IProvisioningAgent agent, IProfileRegistry registry, IProfile profile) { |
| long lastProfileMigrated = getLastMigration(); |
| long lastResetTimestamp = findMostRecentResetTimestamp(registry, profile); |
| return lastProfileMigrated <= lastResetTimestamp; |
| } |
| |
| //The timestamp from which we migrated or -1 |
| private long findMostRecentResetTimestamp(IProfileRegistry registry, IProfile profile) { |
| long[] history = registry.listProfileTimestamps(profile.getProfileId()); |
| int index = history.length - 1; |
| boolean found = false; |
| while (!(found = IProfile.STATE_SHARED_INSTALL_VALUE_BEFOREFLUSH.equals(registry.getProfileStateProperties(profile.getProfileId(), history[index]).get(IProfile.STATE_PROP_SHARED_INSTALL))) && index > 0) { |
| index--; |
| } |
| if (!found) |
| return -1; |
| return history[index]; |
| } |
| |
| private IProfile findMostRecentReset(IProfileRegistry registry, IProfile profile) { |
| long ts = findMostRecentResetTimestamp(registry, profile); |
| if (ts == -1) |
| return null; |
| return registry.getProfile(profile.getProfileId(), ts); |
| } |
| |
| Shell getWorkbenchWindowShell() { |
| IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| return activeWindow != null ? activeWindow.getShell() : null; |
| } |
| |
| public void rememberMigrationCompleted() { |
| IProfileRegistry registry = (IProfileRegistry) ProvisioningUI.getDefaultUI().getSession().getProvisioningAgent().getService(IProfileRegistry.SERVICE_NAME); |
| long[] history = registry.listProfileTimestamps(ProvisioningUI.getDefaultUI().getProfileId()); |
| AutomaticUpdatePlugin.getDefault().getPreferenceStore().setValue(AutomaticUpdateScheduler.MIGRATION_DIALOG_SHOWN, history[history.length - 1]); |
| AutomaticUpdatePlugin.getDefault().savePreferences(); |
| } |
| |
| //Get the timestamp that we migrated from. O if we have not migrated. |
| public long getLastMigration() { |
| return AutomaticUpdatePlugin.getDefault().getPreferenceStore().getLong(AutomaticUpdateScheduler.MIGRATION_DIALOG_SHOWN); |
| } |
| } |