blob: 19833a9a05edc224de5d423577a9a9a3be91303e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Ericsson AB - (Pascal Rapicault)
* Ericsson AB (Hamdan Msheik) - Bug 398833
* Red Hat Inc. - Bug 460967
* Mikael Barbero (Eclipse Foundation) - Bug 498116
*******************************************************************************/
package org.eclipse.equinox.internal.p2.ui.sdk.scheduler;
import java.util.Date;
import java.util.Random;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.garbagecollector.GarbageCollector;
import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.migration.MigrationSupport;
import org.eclipse.equinox.internal.provisional.p2.updatechecker.*;
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.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.IStartup;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* This plug-in is loaded on startup to register with the update checker.
*
* @since 3.5
*/
public class AutomaticUpdateScheduler implements IStartup {
public static final String MIGRATION_DIALOG_SHOWN = "migrationDialogShown"; //$NON-NLS-1$
public static final String P_FUZZY_RECURRENCE = "fuzzy_recurrence"; //$NON-NLS-1$
public static final String[] FUZZY_RECURRENCE = { AutomaticUpdateMessages.SchedulerStartup_OnceADay,
AutomaticUpdateMessages.SchedulerStartup_OnceAWeek, AutomaticUpdateMessages.SchedulerStartup_OnceAMonth };
private static final int ONE_HOUR_IN_MS = 60 * 60 * 1000;
private static final int ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
private IUpdateListener listener = null;
private IUpdateChecker checker = null;
private IProvisioningAgent agent;
@Override
public void earlyStartup() {
AutomaticUpdatePlugin.getDefault().setScheduler(this);
Job updateJob = Job.create("Update Job", e -> { //$NON-NLS-1$
agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class);
IProfileRegistry registry = agent.getService(IProfileRegistry.class);
IProfile currentProfile = registry.getProfile(IProfileRegistry.SELF);
if (currentProfile != null && new MigrationSupport().performMigration(agent, registry, currentProfile)) {
return;
}
removeUnusedPlugins(registry);
scheduleUpdate();
});
updateJob.setSystem(true);
// allow the system to settle
updateJob.schedule(20000);
}
/**
* Invokes the garbage collector to discard unused plugins, if specified by a
* corresponding preference.
*
*/
private void removeUnusedPlugins(IProfileRegistry registry) {
// check if gc is enabled
IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore();
if (!pref.getBoolean(PreferenceConstants.PREF_GC_ON_STARTUP)) {
return;
}
GarbageCollector collector = agent.getService(GarbageCollector.class);
if (collector == null || registry == null) {
return;
}
IProfile profile = registry.getProfile(IProfileRegistry.SELF);
if (profile == null)
return;
collector.runGC(profile);
}
public void shutdown() {
removeUpdateListener();
}
public void rescheduleUpdate() {
removeUpdateListener();
scheduleUpdate();
}
private void scheduleUpdate() {
IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore();
// See if automatic search is enabled at all
if (!pref.getBoolean(PreferenceConstants.PREF_AUTO_UPDATE_ENABLED)) {
return;
}
String schedule = pref.getString(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE);
long delay = IUpdateChecker.ONE_TIME_CHECK;
long poll = IUpdateChecker.ONE_TIME_CHECK;
if (!schedule.equals(PreferenceConstants.PREF_UPDATE_ON_STARTUP)) {
if (schedule.equals(PreferenceConstants.PREF_UPDATE_ON_FUZZY_SCHEDULE)) {
delay = computeFuzzyDelay(pref);
poll = computeFuzzyPoll(pref);
}
}
// We do not access the AutomaticUpdater directly when we register
// the listener. This prevents the UI classes from being started up
// too soon.
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=227582
listener = new IUpdateListener() {
@Override
public void updatesAvailable(UpdateEvent event) {
AutomaticUpdatePlugin.getDefault().getAutomaticUpdater().updatesAvailable(event);
}
@Override
public void checkingForUpdates() {
AutomaticUpdatePlugin.getDefault().getAutomaticUpdater().checkingForUpdates();
}
};
checker = agent.getService(IUpdateChecker.class);
if (checker == null) {
// Something did not initialize properly
IStatus status = new Status(IStatus.ERROR, AutomaticUpdatePlugin.PLUGIN_ID,
AutomaticUpdateMessages.AutomaticUpdateScheduler_UpdateNotInitialized);
StatusManager.getManager().handle(status, StatusManager.LOG);
return;
}
checker.addUpdateCheck(IProfileRegistry.SELF, getProfileQuery(), delay, poll, listener);
}
private IQuery<IInstallableUnit> getProfileQuery() {
// We specifically avoid using the default policy's root property so that we
// don't load all the
// p2 UI classes in doing so.
return new IUProfilePropertyQuery(IProfile.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString());
}
private static long computeFuzzyDelay(IPreferenceStore pref) {
Date nowDate = java.util.Calendar.getInstance().getTime();
long now = nowDate.getTime();
long lastCheckForUpdateSinceEpoch = new LastAutoCheckForUpdateMemo(
AutomaticUpdatePlugin.getDefault().getAgentLocation()).readAndStoreIfAbsent(nowDate).getTime();
long poll = computeFuzzyPoll(pref);
if (now - lastCheckForUpdateSinceEpoch >= poll + getMaxDelay(pref)) {
// Last check for update has exceeded the max delay we allow,
// let's do it sometime in the next hour.
return new Random().nextInt(ONE_HOUR_IN_MS);
}
long delay = now - lastCheckForUpdateSinceEpoch;
// We do delay the next check sometime in the 8 hours after the computed
// schedule
return poll - delay + new Random().nextInt(8 * ONE_HOUR_IN_MS);
}
private static long getMaxDelay(IPreferenceStore pref) {
String recurrence = pref.getString(P_FUZZY_RECURRENCE);
if (AutomaticUpdateMessages.SchedulerStartup_OnceADay.equals(recurrence)) {
return 6 * ONE_HOUR_IN_MS;
} else if (AutomaticUpdateMessages.SchedulerStartup_OnceAWeek.equals(recurrence)) {
return 2 * ONE_DAY_IN_MS;
} else { // Once a month
return 6 * ONE_DAY_IN_MS;
}
}
private static long computeFuzzyPoll(IPreferenceStore pref) {
String recurrence = pref.getString(P_FUZZY_RECURRENCE);
if (AutomaticUpdateMessages.SchedulerStartup_OnceADay.equals(recurrence)) {
return ONE_DAY_IN_MS;
} else if (AutomaticUpdateMessages.SchedulerStartup_OnceAWeek.equals(recurrence)) {
return 7 * ONE_DAY_IN_MS;
} else { // Once a month
// It's not rocket science we're doing here,
// let's approximate that a month is always 30 days long
return 30 * ONE_DAY_IN_MS;
}
}
private void removeUpdateListener() {
// Remove the current listener if there is one
if (listener != null && checker != null) {
checker.removeUpdateCheck(listener);
listener = null;
}
}
}