1 /******************************************************************************* 2 * Copyright (c) 2008, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Ericsson AB - (Pascal Rapicault) 14 * Ericsson AB (Hamdan Msheik) - Bug 398833 15 * Red Hat Inc. - Bug 460967 16 * Mikael Barbero (Eclipse Foundation) - Bug 498116 17 *******************************************************************************/ 18 package org.eclipse.equinox.internal.p2.ui.sdk.scheduler; 19 20 import java.util.Date; 21 import java.util.Random; 22 import org.eclipse.core.runtime.IStatus; 23 import org.eclipse.core.runtime.Status; 24 import org.eclipse.core.runtime.jobs.Job; 25 import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper; 26 import org.eclipse.equinox.internal.p2.garbagecollector.GarbageCollector; 27 import org.eclipse.equinox.internal.p2.ui.sdk.scheduler.migration.MigrationSupport; 28 import org.eclipse.equinox.internal.provisional.p2.updatechecker.*; 29 import org.eclipse.equinox.p2.core.IProvisioningAgent; 30 import org.eclipse.equinox.p2.engine.IProfile; 31 import org.eclipse.equinox.p2.engine.IProfileRegistry; 32 import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery; 33 import org.eclipse.equinox.p2.metadata.IInstallableUnit; 34 import org.eclipse.equinox.p2.query.IQuery; 35 import org.eclipse.jface.preference.IPreferenceStore; 36 import org.eclipse.ui.IStartup; 37 import org.eclipse.ui.statushandlers.StatusManager; 38 39 /** 40 * This plug-in is loaded on startup to register with the update checker. 41 * 42 * @since 3.5 43 */ 44 public class AutomaticUpdateScheduler implements IStartup { 45 public static final String MIGRATION_DIALOG_SHOWN = "migrationDialogShown"; //$NON-NLS-1$ 46 47 public static final String P_FUZZY_RECURRENCE = "fuzzy_recurrence"; //$NON-NLS-1$ 48 49 public static final String[] FUZZY_RECURRENCE = { AutomaticUpdateMessages.SchedulerStartup_OnceADay, 50 AutomaticUpdateMessages.SchedulerStartup_OnceAWeek, AutomaticUpdateMessages.SchedulerStartup_OnceAMonth }; 51 52 private static final int ONE_HOUR_IN_MS = 60 * 60 * 1000; 53 private static final int ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS; 54 55 private IUpdateListener listener = null; 56 private IUpdateChecker checker = null; 57 58 private IProvisioningAgent agent; 59 60 @Override earlyStartup()61 public void earlyStartup() { 62 AutomaticUpdatePlugin.getDefault().setScheduler(this); 63 64 Job updateJob = Job.create("Update Job", e -> { //$NON-NLS-1$ 65 agent = ServiceHelper.getService(AutomaticUpdatePlugin.getContext(), IProvisioningAgent.class); 66 IProfileRegistry registry = agent.getService(IProfileRegistry.class); 67 IProfile currentProfile = registry.getProfile(IProfileRegistry.SELF); 68 if (currentProfile != null && new MigrationSupport().performMigration(agent, registry, currentProfile)) { 69 return; 70 } 71 72 removeUnusedPlugins(registry); 73 scheduleUpdate(); 74 }); 75 updateJob.setSystem(true); 76 // allow the system to settle 77 updateJob.schedule(20000); 78 79 } 80 81 /** 82 * Invokes the garbage collector to discard unused plugins, if specified by a 83 * corresponding preference. 84 * 85 */ removeUnusedPlugins(IProfileRegistry registry)86 private void removeUnusedPlugins(IProfileRegistry registry) { 87 // check if gc is enabled 88 IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); 89 if (!pref.getBoolean(PreferenceConstants.PREF_GC_ON_STARTUP)) { 90 return; 91 } 92 GarbageCollector collector = agent.getService(GarbageCollector.class); 93 if (collector == null || registry == null) { 94 return; 95 } 96 IProfile profile = registry.getProfile(IProfileRegistry.SELF); 97 if (profile == null) 98 return; 99 collector.runGC(profile); 100 } 101 shutdown()102 public void shutdown() { 103 removeUpdateListener(); 104 } 105 rescheduleUpdate()106 public void rescheduleUpdate() { 107 removeUpdateListener(); 108 scheduleUpdate(); 109 } 110 scheduleUpdate()111 private void scheduleUpdate() { 112 IPreferenceStore pref = AutomaticUpdatePlugin.getDefault().getPreferenceStore(); 113 // See if automatic search is enabled at all 114 if (!pref.getBoolean(PreferenceConstants.PREF_AUTO_UPDATE_ENABLED)) { 115 return; 116 } 117 String schedule = pref.getString(PreferenceConstants.PREF_AUTO_UPDATE_SCHEDULE); 118 long delay = IUpdateChecker.ONE_TIME_CHECK; 119 long poll = IUpdateChecker.ONE_TIME_CHECK; 120 if (!schedule.equals(PreferenceConstants.PREF_UPDATE_ON_STARTUP)) { 121 if (schedule.equals(PreferenceConstants.PREF_UPDATE_ON_FUZZY_SCHEDULE)) { 122 delay = computeFuzzyDelay(pref); 123 poll = computeFuzzyPoll(pref); 124 } 125 } 126 // We do not access the AutomaticUpdater directly when we register 127 // the listener. This prevents the UI classes from being started up 128 // too soon. 129 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=227582 130 listener = new IUpdateListener() { 131 @Override 132 public void updatesAvailable(UpdateEvent event) { 133 AutomaticUpdatePlugin.getDefault().getAutomaticUpdater().updatesAvailable(event); 134 } 135 136 @Override 137 public void checkingForUpdates() { 138 AutomaticUpdatePlugin.getDefault().getAutomaticUpdater().checkingForUpdates(); 139 } 140 }; 141 142 checker = agent.getService(IUpdateChecker.class); 143 if (checker == null) { 144 // Something did not initialize properly 145 IStatus status = new Status(IStatus.ERROR, AutomaticUpdatePlugin.PLUGIN_ID, 146 AutomaticUpdateMessages.AutomaticUpdateScheduler_UpdateNotInitialized); 147 StatusManager.getManager().handle(status, StatusManager.LOG); 148 return; 149 } 150 checker.addUpdateCheck(IProfileRegistry.SELF, getProfileQuery(), delay, poll, listener); 151 152 } 153 getProfileQuery()154 private IQuery<IInstallableUnit> getProfileQuery() { 155 // We specifically avoid using the default policy's root property so that we 156 // don't load all the 157 // p2 UI classes in doing so. 158 return new IUProfilePropertyQuery(IProfile.PROP_PROFILE_ROOT_IU, Boolean.TRUE.toString()); 159 } 160 computeFuzzyDelay(IPreferenceStore pref)161 private static long computeFuzzyDelay(IPreferenceStore pref) { 162 Date nowDate = java.util.Calendar.getInstance().getTime(); 163 long now = nowDate.getTime(); 164 long lastCheckForUpdateSinceEpoch = new LastAutoCheckForUpdateMemo( 165 AutomaticUpdatePlugin.getDefault().getAgentLocation()).readAndStoreIfAbsent(nowDate).getTime(); 166 long poll = computeFuzzyPoll(pref); 167 if (now - lastCheckForUpdateSinceEpoch >= poll + getMaxDelay(pref)) { 168 // Last check for update has exceeded the max delay we allow, 169 // let's do it sometime in the next hour. 170 return new Random().nextInt(ONE_HOUR_IN_MS); 171 } 172 long delay = now - lastCheckForUpdateSinceEpoch; 173 // We do delay the next check sometime in the 8 hours after the computed 174 // schedule 175 return poll - delay + new Random().nextInt(8 * ONE_HOUR_IN_MS); 176 } 177 getMaxDelay(IPreferenceStore pref)178 private static long getMaxDelay(IPreferenceStore pref) { 179 String recurrence = pref.getString(P_FUZZY_RECURRENCE); 180 if (AutomaticUpdateMessages.SchedulerStartup_OnceADay.equals(recurrence)) { 181 return 6 * ONE_HOUR_IN_MS; 182 } else if (AutomaticUpdateMessages.SchedulerStartup_OnceAWeek.equals(recurrence)) { 183 return 2 * ONE_DAY_IN_MS; 184 } else { // Once a month 185 return 6 * ONE_DAY_IN_MS; 186 } 187 } 188 computeFuzzyPoll(IPreferenceStore pref)189 private static long computeFuzzyPoll(IPreferenceStore pref) { 190 String recurrence = pref.getString(P_FUZZY_RECURRENCE); 191 if (AutomaticUpdateMessages.SchedulerStartup_OnceADay.equals(recurrence)) { 192 return ONE_DAY_IN_MS; 193 } else if (AutomaticUpdateMessages.SchedulerStartup_OnceAWeek.equals(recurrence)) { 194 return 7 * ONE_DAY_IN_MS; 195 } else { // Once a month 196 // It's not rocket science we're doing here, 197 // let's approximate that a month is always 30 days long 198 return 30 * ONE_DAY_IN_MS; 199 } 200 } 201 removeUpdateListener()202 private void removeUpdateListener() { 203 // Remove the current listener if there is one 204 if (listener != null && checker != null) { 205 checker.removeUpdateCheck(listener); 206 listener = null; 207 } 208 } 209 } 210