| /******************************************************************************* |
| * Copyright (c) 2007, 2015 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.garbagecollector; |
| |
| import java.util.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.runtime.preferences.*; |
| import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; |
| import org.eclipse.equinox.internal.p2.engine.*; |
| import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; |
| import org.eclipse.equinox.internal.provisional.p2.core.eventbus.SynchronousProvisioningListener; |
| import org.eclipse.equinox.p2.core.IProvisioningAgent; |
| import org.eclipse.equinox.p2.core.spi.IAgentService; |
| import org.eclipse.equinox.p2.engine.IProfile; |
| import org.eclipse.equinox.p2.engine.IProfileRegistry; |
| import org.eclipse.equinox.p2.metadata.IArtifactKey; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; |
| import org.osgi.service.prefs.Preferences; |
| |
| /** |
| * The main control point for the p2 garbage collector. Takes a Profile and runs the CoreGarbageCollector with the |
| * appropriate MarkSets for the repositories used by that Profile. |
| * |
| * Takes the profile passed in and creates a set (markSet) that maps the artifact repositories it uses to the |
| * artifact keys its IUs hold. This is done by getting MarkSets from all registered IMarkSetProviders. |
| * |
| * Then, the MarkSets are obtained for every other registered Profile in a similar fashion. Each MarkSet is |
| * checked to see if its artifact repository is already a key in markSet. If so, that MarkSet's artifact keys |
| * are added to the list that is mapped to by the artifact repository. |
| */ |
| public class GarbageCollector implements SynchronousProvisioningListener, IAgentService { |
| /** |
| * Service name constant for the garbage collection service. |
| */ |
| public static final String SERVICE_NAME = GarbageCollector.class.getName(); |
| |
| private class ParameterizedSafeRunnable implements ISafeRunnable { |
| IProfile aProfile; |
| MarkSet[] aProfileMarkSets; |
| IConfigurationElement cfg; |
| |
| public ParameterizedSafeRunnable(IConfigurationElement runtAttribute, IProfile profile) { |
| cfg = runtAttribute; |
| aProfile = profile; |
| } |
| |
| public MarkSet[] getResult() { |
| return aProfileMarkSets; |
| } |
| |
| public void handleException(Throwable exception) { |
| LogHelper.log(new Status(IStatus.ERROR, GCActivator.ID, Messages.Error_in_extension, exception)); |
| } |
| |
| public void run() throws Exception { |
| MarkSetProvider aMarkSetProvider = (MarkSetProvider) cfg.createExecutableExtension(ATTRIBUTE_CLASS); |
| if (aMarkSetProvider == null) { |
| aProfileMarkSets = null; |
| return; |
| } |
| aProfileMarkSets = aMarkSetProvider.getMarkSets(agent, aProfile); |
| } |
| } |
| |
| private static final String ATTRIBUTE_CLASS = "class"; //$NON-NLS-1$ |
| |
| private static final String PT_MARKSET = GCActivator.ID + ".marksetproviders"; //$NON-NLS-1$ |
| final IProvisioningAgent agent; |
| |
| //The GC is triggered when an uninstall event occurred during a "transaction" and the transaction is committed. |
| String uninstallEventProfileId = null; |
| |
| /** |
| * Maps IArtifactRepository objects to their respective "marked set" of IArtifactKeys |
| */ |
| private Map<IArtifactRepository, Collection<IArtifactKey>> markSet; |
| |
| public GarbageCollector(IProvisioningAgent agent) { |
| this.agent = agent; |
| } |
| |
| private void addKeys(Collection<IArtifactKey> keyList, IArtifactKey[] keyArray) { |
| for (int i = 0; i < keyArray.length; i++) |
| keyList.add(keyArray[i]); |
| } |
| |
| private void contributeMarkSets(IConfigurationElement runAttribute, IProfile profile, boolean addRepositories) { |
| ParameterizedSafeRunnable providerExecutor = new ParameterizedSafeRunnable(runAttribute, profile); |
| SafeRunner.run(providerExecutor); |
| MarkSet[] aProfileMarkSets = providerExecutor.getResult(); |
| if (aProfileMarkSets == null || aProfileMarkSets.length == 0 || aProfileMarkSets[0] == null) |
| return; |
| |
| for (int i = 0; i < aProfileMarkSets.length; i++) { |
| if (aProfileMarkSets[i] == null) { |
| continue; |
| } |
| Collection<IArtifactKey> keys = markSet.get(aProfileMarkSets[i].getRepo()); |
| if (keys == null) { |
| if (addRepositories) { |
| keys = new HashSet<IArtifactKey>(); |
| markSet.put(aProfileMarkSets[i].getRepo(), keys); |
| addKeys(keys, aProfileMarkSets[i].getKeys()); |
| } |
| } else { |
| addKeys(keys, aProfileMarkSets[i].getKeys()); |
| } |
| } |
| } |
| |
| protected boolean getBooleanPreference(String key, boolean defaultValue) { |
| IPreferencesService prefService = GCActivator.getService(IPreferencesService.class); |
| if (prefService == null) |
| return defaultValue; |
| List<IEclipsePreferences> nodes = new ArrayList<IEclipsePreferences>(); |
| // todo we should look in the instance scope as well but have to be careful that the instance location has been set |
| nodes.add(ConfigurationScope.INSTANCE.getNode(GCActivator.ID)); |
| nodes.add(DefaultScope.INSTANCE.getNode(GCActivator.ID)); |
| return Boolean.parseBoolean(prefService.get(key, Boolean.toString(defaultValue), nodes.toArray(new Preferences[nodes.size()]))); |
| } |
| |
| private void invokeCoreGC() { |
| for (IArtifactRepository nextRepo : markSet.keySet()) { |
| IArtifactKey[] keys = markSet.get(nextRepo).toArray(new IArtifactKey[0]); |
| MarkSet aMarkSet = new MarkSet(keys, nextRepo); |
| new CoreGarbageCollector().clean(aMarkSet.getKeys(), aMarkSet.getRepo()); |
| } |
| } |
| |
| public void notify(EventObject o) { |
| if (o instanceof InstallableUnitEvent) { |
| InstallableUnitEvent event = (InstallableUnitEvent) o; |
| if (event.isUninstall() && event.isPost()) { |
| uninstallEventProfileId = event.getProfile().getProfileId(); |
| } |
| } else if (o instanceof CommitOperationEvent) { |
| if (uninstallEventProfileId != null) { |
| CommitOperationEvent event = (CommitOperationEvent) o; |
| if (uninstallEventProfileId.equals(event.getProfile().getProfileId()) && getBooleanPreference(GCActivator.GC_ENABLED, true)) |
| runGC(event.getProfile()); |
| uninstallEventProfileId = null; |
| } |
| } else if (o instanceof RollbackOperationEvent) { |
| if (uninstallEventProfileId != null && uninstallEventProfileId.equals(((RollbackOperationEvent) o).getProfile().getProfileId())) |
| uninstallEventProfileId = null; |
| } |
| } |
| |
| public void runGC(IProfile profile) { |
| markSet = new HashMap<IArtifactRepository, Collection<IArtifactKey>>(); |
| if (!traverseMainProfile(profile)) |
| return; |
| |
| //Complete each MarkSet with the MarkSets provided by all of the other registered Profiles |
| traverseRegisteredProfiles(); |
| |
| //Run the GC on each MarkSet |
| invokeCoreGC(); |
| } |
| |
| /*(non-Javadoc) |
| * @see org.eclipse.equinox.p2.core.spi.IAgentService#start() |
| */ |
| public void start() { |
| IProvisioningEventBus eventBus = (IProvisioningEventBus) agent.getService(IProvisioningEventBus.SERVICE_NAME); |
| if (eventBus == null) |
| return; |
| eventBus.addListener(this); |
| } |
| |
| /*(non-Javadoc) |
| * @see org.eclipse.equinox.p2.core.spi.IAgentService#stop() |
| */ |
| public void stop() { |
| IProvisioningEventBus eventBus = (IProvisioningEventBus) agent.getService(IProvisioningEventBus.SERVICE_NAME); |
| if (eventBus != null) |
| eventBus.removeListener(this); |
| } |
| |
| private boolean traverseMainProfile(IProfile profile) { |
| IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| IConfigurationElement[] configElts = registry.getConfigurationElementsFor(PT_MARKSET); |
| |
| //First we collect all repos and keys for the profile being GC'ed |
| for (int i = 0; i < configElts.length; i++) { |
| if (!(configElts[i].getName().equals("run"))) { //$NON-NLS-1$ |
| continue; |
| } |
| IConfigurationElement runAttribute = configElts[i]; |
| if (runAttribute == null) { |
| continue; |
| } |
| |
| contributeMarkSets(runAttribute, profile, true); |
| } |
| return true; |
| } |
| |
| private void traverseRegisteredProfiles() { |
| IExtensionRegistry registry = RegistryFactory.getRegistry(); |
| IConfigurationElement[] configElts = registry.getConfigurationElementsFor(PT_MARKSET); |
| for (int i = 0; i < configElts.length; i++) { |
| if (!(configElts[i].getName().equals("run"))) { //$NON-NLS-1$ |
| continue; |
| } |
| IConfigurationElement runAttribute = configElts[i]; |
| if (runAttribute == null) { |
| continue; |
| } |
| |
| IProfileRegistry profileRegistry = (IProfileRegistry) agent.getService(IProfileRegistry.SERVICE_NAME); |
| if (profileRegistry == null) |
| return; |
| IProfile[] registeredProfiles = profileRegistry.getProfiles(); |
| |
| for (int j = 0; j < registeredProfiles.length; j++) { |
| contributeMarkSets(runAttribute, registeredProfiles[j], false); |
| } |
| } |
| } |
| } |