/******************************************************************************* * Copyright (c) 2008, 2010 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 * Sonatype, Inc. - ongoing development *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.sdk.scheduler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.*; import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils; import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; import org.eclipse.equinox.internal.provisional.p2.core.eventbus.ProvisioningListener; import org.eclipse.equinox.internal.provisional.p2.updatechecker.IUpdateListener; import org.eclipse.equinox.internal.provisional.p2.updatechecker.UpdateEvent; import org.eclipse.equinox.p2.engine.*; import org.eclipse.equinox.p2.engine.query.UserVisibleRootQuery; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.ui.ProvisioningUI; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.*; import org.eclipse.ui.*; import org.eclipse.ui.statushandlers.StatusManager; /** * @since 3.5 */ public class AutomaticUpdater implements IUpdateListener { StatusLineCLabelContribution updateAffordance; IStatusLineManager statusLineManager; Collection<IInstallableUnit> iusWithUpdates; String profileId; ProvisioningListener profileListener; AutomaticUpdatesPopup popup; boolean alreadyDownloaded = false; UpdateOperation operation; private static final String AUTO_UPDATE_STATUS_ITEM = "AutoUpdatesStatus"; //$NON-NLS-1$ public AutomaticUpdater() { createProfileListener(); } private void createProfileListener() { profileListener = new ProvisioningListener() { public void notify(EventObject o) { if (o instanceof IProfileEvent) { IProfileEvent event = (IProfileEvent) o; if (event.getReason() == IProfileEvent.CHANGED && sameProfile(event.getProfileId())) { triggerNewUpdateNotification(); } } } }; getProvisioningEventBus().addListener(profileListener); } boolean sameProfile(String another) { if (another.equals(IProfileRegistry.SELF)) { another = getProfileRegistry().getProfile(another).getProfileId(); } if (profileId.equals(IProfileRegistry.SELF)) { profileId = getProfileRegistry().getProfile(profileId).getProfileId(); } return profileId.equals(another); } /* * (non-Javadoc) * * @see * org.eclipse.equinox.internal.provisional.p2.updatechecker.IUpdateListener * # * updatesAvailable(org.eclipse.equinox.internal.provisional.p2.updatechecker * .UpdateEvent) */ public void updatesAvailable(UpdateEvent event) { updatesAvailable(event, true); } void updatesAvailable(final UpdateEvent event, final boolean notifyWithPopup) { final boolean download = getPreferenceStore().getBoolean(PreferenceConstants.PREF_DOWNLOAD_ONLY); profileId = event.getProfileId(); iusWithUpdates = event.getIUs(); validateIusToUpdate(); alreadyDownloaded = false; // Create an update operation to reflect the new updates that are available. operation = new UpdateOperation(getSession(), iusWithUpdates); operation.setProfileId(event.getProfileId()); IStatus status = operation.resolveModal(new NullProgressMonitor()); if (!status.isOK() || operation.getPossibleUpdates() == null || operation.getPossibleUpdates().length == 0) { if (PlatformUI.isWorkbenchRunning()) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { public void run() { clearUpdateAffordances(); } }); } return; } // Download the items before notifying user if the // preference dictates. if (download) { ProfileModificationJob job = new ProfileModificationJob(AutomaticUpdateMessages.AutomaticUpdater_AutomaticDownloadOperationName, getSession(), event.getProfileId(), operation.getProvisioningPlan(), new ProvisioningContext(getSession().getProvisioningAgent())); job.setPhaseSet(PhaseSetFactory.createPhaseSetIncluding(new String[] {PhaseSetFactory.PHASE_COLLECT})); job.setUser(false); job.setSystem(true); job.addJobChangeListener(new JobChangeAdapter() { public void done(IJobChangeEvent jobEvent) { IStatus jobStatus = jobEvent.getResult(); if (jobStatus.isOK()) { alreadyDownloaded = true; PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { public void run() { notifyUserOfUpdates(operation.getResolutionResult().isOK(), notifyWithPopup); } }); } else if (jobStatus.getSeverity() != IStatus.CANCEL) { StatusManager.getManager().handle(jobStatus, StatusManager.LOG); } } }); job.schedule(); } else { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { public void run() { notifyUserOfUpdates(operation.getResolutionResult().isOK(), notifyWithPopup); } }); } } ProvisioningSession getSession() { return AutomaticUpdatePlugin.getDefault().getSession(); } /* * Use with caution, as this still start the whole UI bundle. Shouldn't be used * in any of the update checking code, only the code that presents updates when notified. */ ProvisioningUI getProvisioningUI() { return ProvisioningUI.getDefaultUI(); } /* * Filter out the ius that aren't visible to the user or are * locked for updating. */ void validateIusToUpdate() { ArrayList<IInstallableUnit> list = new ArrayList<IInstallableUnit>(iusWithUpdates.size()); IProfile profile = getProfileRegistry().getProfile(profileId); for (IInstallableUnit iuWithUpdate : iusWithUpdates) { try { if (validToUpdate(profile, iuWithUpdate)) list.add(iuWithUpdate); } catch (OperationCanceledException e) { // Nothing to report } } iusWithUpdates = list; } // A proposed update is valid if it is still visible to the user as an // installed item (it is a root) // and if it is not locked for updating. private boolean validToUpdate(IProfile profile, IInstallableUnit iu) { int lock = IProfile.LOCK_NONE; boolean isRoot = false; try { String value = profile.getInstallableUnitProperty(iu, IProfile.PROP_PROFILE_LOCKED_IU); if (value != null) lock = Integer.parseInt(value); value = profile.getInstallableUnitProperty(iu, IProfile.PROP_PROFILE_ROOT_IU); isRoot = value == null ? false : Boolean.valueOf(value).booleanValue(); } catch (NumberFormatException e) { // ignore and assume no lock } return isRoot && (lock & IProfile.LOCK_UPDATE) == 0; } Shell getWorkbenchWindowShell() { IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); return activeWindow != null ? activeWindow.getShell() : null; } IStatusLineManager getStatusLineManager() { if (statusLineManager != null) return statusLineManager; IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (activeWindow == null) return null; // YUCK! YUCK! YUCK! // IWorkbenchWindow does not define getStatusLineManager(), yet // WorkbenchWindow does try { Method method = activeWindow.getClass().getDeclaredMethod("getStatusLineManager", new Class[0]); //$NON-NLS-1$ try { Object statusLine = method.invoke(activeWindow, new Object[0]); if (statusLine instanceof IStatusLineManager) { statusLineManager = (IStatusLineManager) statusLine; return statusLineManager; } } catch (InvocationTargetException e) { // oh well } catch (IllegalAccessException e) { // I tried } } catch (NoSuchMethodException e) { // can't blame us for trying. } IWorkbenchPartSite site = activeWindow.getActivePage().getActivePart().getSite(); if (site instanceof IViewSite) { statusLineManager = ((IViewSite) site).getActionBars().getStatusLineManager(); } else if (site instanceof IEditorSite) { statusLineManager = ((IEditorSite) site).getActionBars().getStatusLineManager(); } return statusLineManager; } void updateStatusLine() { IStatusLineManager manager = getStatusLineManager(); if (manager != null) manager.update(true); } void createUpdateAffordance() { updateAffordance = new StatusLineCLabelContribution(AUTO_UPDATE_STATUS_ITEM, 5); updateAffordance.addListener(SWT.MouseDown, new Listener() { public void handleEvent(Event event) { launchUpdate(); } }); IStatusLineManager manager = getStatusLineManager(); if (manager != null) { manager.add(updateAffordance); manager.update(true); } } void notifyUserOfUpdates(boolean isValid, boolean showPopup) { if (updateAffordance == null) createUpdateAffordance(); if (isValid) { if (showPopup) openUpdatePopup(); updateAffordance.setTooltip(AutomaticUpdateMessages.AutomaticUpdater_ClickToReviewUpdates); updateAffordance.setImage(AutomaticUpdatePlugin.getDefault().getImageRegistry().get((AutomaticUpdatePlugin.IMG_TOOL_UPDATE))); } else { updateAffordance.setTooltip(AutomaticUpdateMessages.AutomaticUpdater_ClickToReviewUpdatesWithProblems); updateAffordance.setImage(AutomaticUpdatePlugin.getDefault().getImageRegistry().get((AutomaticUpdatePlugin.IMG_TOOL_UPDATE_PROBLEMS))); } IStatusLineManager manager = getStatusLineManager(); if (manager != null) { manager.update(true); } } void checkUpdateAffordanceEnablement() { // We don't currently support enablement in the affordance, // so we hide it if it should not be enabled. if (updateAffordance == null) return; boolean shouldBeVisible = getProvisioningUI().hasScheduledOperations(); if (updateAffordance.isVisible() != shouldBeVisible) { IStatusLineManager manager = getStatusLineManager(); if (manager != null) { updateAffordance.setVisible(shouldBeVisible); manager.update(true); } } } void openUpdatePopup() { if (popup == null) popup = new AutomaticUpdatesPopup(getWorkbenchWindowShell(), alreadyDownloaded, getPreferenceStore()); popup.open(); } void clearUpdateAffordances() { if (updateAffordance != null) { IStatusLineManager manager = getStatusLineManager(); if (manager != null) { manager.remove(updateAffordance); manager.update(true); } updateAffordance.dispose(); updateAffordance = null; } if (popup != null) { popup.close(false); popup = null; } } public void launchUpdate() { getProvisioningUI().openUpdateWizard(false, operation, null); } /* * The profile has changed. Make sure our toUpdate list is still valid and * if there is nothing to update, get rid of the update popup and * affordance. */ void triggerNewUpdateNotification() { Job notifyJob = new Job("Update validate job") { //$NON-NLS-1$ public IStatus run(IProgressMonitor monitor) { if (monitor.isCanceled()) return Status.CANCEL_STATUS; // notify that updates are available for all roots. We don't know for sure that // there are any, but this will cause everything to be rechecked. Don't trigger // a popup, just update the affordance and internal state. updatesAvailable(new UpdateEvent(profileId, getInstalledIUs())); return Status.OK_STATUS; } }; notifyJob.setSystem(true); notifyJob.setUser(false); notifyJob.setPriority(Job.LONG); notifyJob.schedule(); } /* * Get the IInstallable units for the specified profile * * @param profileId the profile in question * @param all <code>true</code> if all IInstallableUnits in the profile should * be returned, <code>false</code> only those IInstallableUnits marked as (user visible) roots * should be returned. * * @return an array of IInstallableUnits installed in the profile. */ public Collection<IInstallableUnit> getInstalledIUs() { IProfile profile = getProfileRegistry().getProfile(profileId); if (profile == null) return CollectionUtils.emptyList(); IQuery<IInstallableUnit> query = new UserVisibleRootQuery(); IQueryResult<IInstallableUnit> queryResult = profile.query(query, null); return queryResult.toUnmodifiableSet(); } public void shutdown() { statusLineManager = null; if (profileListener != null) { getProvisioningEventBus().removeListener(profileListener); profileListener = null; } } IProfileRegistry getProfileRegistry() { return (IProfileRegistry) getSession().getProvisioningAgent().getService(IProfileRegistry.SERVICE_NAME); } IProvisioningEventBus getProvisioningEventBus() { return (IProvisioningEventBus) getSession().getProvisioningAgent().getService(IProvisioningEventBus.SERVICE_NAME); } IPreferenceStore getPreferenceStore() { return AutomaticUpdatePlugin.getDefault().getPreferenceStore(); } }