Merge branch 'develop' into remoting-4.0
[taxeditor.git] / eu.etaxonomy.taxeditor.application / src / main / java / eu / etaxonomy / taxeditor / update / P2Util.java
1 package eu.etaxonomy.taxeditor.update;
2
3 import java.net.URI;
4 import java.net.URISyntaxException;
5 import java.util.ArrayList;
6 import java.util.List;
7
8 import org.eclipse.core.runtime.IProgressMonitor;
9 import org.eclipse.core.runtime.IStatus;
10 import org.eclipse.core.runtime.OperationCanceledException;
11 import org.eclipse.core.runtime.Status;
12 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
13 import org.eclipse.core.runtime.jobs.Job;
14 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
15 import org.eclipse.equinox.internal.p2.ui.ProvUI;
16 import org.eclipse.equinox.internal.p2.ui.model.ElementUtils;
17 import org.eclipse.equinox.internal.p2.ui.model.MetadataRepositoryElement;
18 import org.eclipse.equinox.p2.core.IProvisioningAgent;
19 import org.eclipse.equinox.p2.core.ProvisionException;
20 import org.eclipse.equinox.p2.operations.ProvisioningJob;
21 import org.eclipse.equinox.p2.operations.ProvisioningSession;
22 import org.eclipse.equinox.p2.operations.Update;
23 import org.eclipse.equinox.p2.operations.UpdateOperation;
24 import org.eclipse.equinox.p2.repository.IRepository;
25 import org.eclipse.equinox.p2.repository.IRepositoryManager;
26 import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
27 import org.eclipse.equinox.p2.ui.ProvisioningUI;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.ui.PlatformUI;
30 import org.osgi.framework.BundleContext;
31 import org.osgi.framework.ServiceReference;
32
33 import eu.etaxonomy.taxeditor.TaxonomicEditorPlugin;
34 import eu.etaxonomy.taxeditor.model.MessagingUtils;
35 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
36 import eu.etaxonomy.taxeditor.util.ApplicationUtil;
37
38 /**
39 * This class is a utility class for updating the editor from a p2 update site,
40 * greatly inspired by the links given below.
41 *
42 *
43 * To Do :
44 * - Allow configurable update sites
45 *
46 * Notes :
47 *
48 *
49 * @see http://wiki.eclipse.org/Equinox/p2/Adding_Self-Update_to_an_RCP_Application
50 * @see http://bugs.eclipse.org/281226
51 * @see http://www.vogella.com/tutorials/EclipseP2Update/article.html
52 */
53 public class P2Util {
54
55 //private static String LOCAL_UPDATE_SITE = "file:///path/.../to/Development/EDIT/taxeditor/eu.etaxonomy.taxeditor/target/repository/";
56
57 private static String EDIT_NIGHTLY_UPDATE_SITE = "http://cybertaxonomy.eu/download/taxeditor/update/nightly/";
58 private static String EDIT_NIGHTLY_UPDATE_SITE_NAME = "Taxonomic Editor Nightly";
59
60 private static String EDIT_SNAPSHOT_UPDATE_SITE = "http://cybertaxonomy.eu/download/taxeditor/update/snapshot/";
61 private static String EDIT_SNAPSHOT_UPDATE_SITE_NAME = "Taxonomic Editor Snapshot";
62
63 private static String EDIT_STABLE_UPDATE_SITE = "http://cybertaxonomy.eu/download/taxeditor/update/stable/";
64 private static String EDIT_STABLE_UPDATE_SITE_NAME = "Taxonomic Editor Stable";
65
66 /**
67 * Retrieve and load the saved list of repositories from the preference store,
68 * making sure that at least the default repository is always loaded.
69 */
70 @SuppressWarnings("restriction")
71 public static void setP2UpdateRepositories() {
72 String updateSite = EDIT_NIGHTLY_UPDATE_SITE;
73 String updateSiteName = EDIT_NIGHTLY_UPDATE_SITE_NAME;
74
75 if(ApplicationUtil.isStable()) {
76 updateSite = EDIT_STABLE_UPDATE_SITE;
77 updateSiteName = EDIT_STABLE_UPDATE_SITE_NAME;
78 }
79 List<MetadataRepositoryElement> repoElements = new ArrayList<MetadataRepositoryElement>();
80 List<MetadataRepositoryElement> savedRepoElements = PreferencesUtil.getP2Repositories();
81 if(savedRepoElements.isEmpty()) {
82 // we always need an update site, so we add the default one
83 try {
84 MetadataRepositoryElement element = new MetadataRepositoryElement(null, new URI(updateSite), true);
85 element.setNickname(updateSiteName);
86 repoElements.add(element);
87 } catch (URISyntaxException e) {
88 MessagingUtils.errorDialog("Invalid update site URI",
89 P2Util.class,
90 "The update site URI has an invalid syntax",
91 TaxonomicEditorPlugin.PLUGIN_ID,
92 e,
93 false);
94 }
95 }
96 repoElements.addAll(savedRepoElements);
97
98 ElementUtils.updateRepositoryUsingElements(ProvisioningUI.getDefaultUI(),repoElements
99 .toArray(new MetadataRepositoryElement[]{} ), null);
100
101 }
102
103 /**
104 * {@link org.eclipse.equinox.p2.ui.RepositoryManipulationPage} which handles the repsitory site list
105 * in preferences does not create a preference store and hence the changes are not saved. This means
106 * that we need to save it ourselves.
107 *
108 * This method saves the list of current repositories in the preference store as a string with
109 * specific delimiters.
110 */
111
112 public static void saveP2RepositoryPreferences() {
113
114 IMetadataRepositoryManager metaManager = ProvUI.getMetadataRepositoryManager(ProvisioningUI.getDefaultUI().getSession());
115
116 URI[] currentlyEnabled = metaManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_ALL);
117 URI[] currentlyDisabled = metaManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_DISABLED);
118
119 List<MetadataRepositoryElement> repoElements = new ArrayList<MetadataRepositoryElement>();
120
121 for(URI repo : currentlyEnabled) {
122 boolean enabled = true;
123 String nickname = metaManager.getRepositoryProperty(repo, IRepository.PROP_NICKNAME);
124 MetadataRepositoryElement element = new MetadataRepositoryElement(null, repo, true);
125 element.setNickname(nickname);
126 element.setEnabled(enabled);
127 repoElements.add(element);
128 }
129
130 for(URI repo : currentlyDisabled) {
131 boolean enabled = false;
132 String nickname = metaManager.getRepositoryProperty(repo, IRepository.PROP_NICKNAME);
133 MetadataRepositoryElement element = new MetadataRepositoryElement(null, repo, true);
134 element.setNickname(nickname);
135 element.setEnabled(enabled);
136 repoElements.add(element);
137 }
138 PreferencesUtil.setP2Repositories(repoElements);
139 }
140
141 /**
142 *
143 *
144 */
145 public static void checkForUpdates() {
146
147 Job updateJob = new Job("Update Job") {
148 @Override
149 public IStatus run(IProgressMonitor monitor) {
150 return doCheckForUpdates(monitor);
151 }
152 };
153 updateJob.schedule();
154 }
155
156 /**
157 * @param monitor
158 * @return
159 */
160 private static IStatus doCheckForUpdates(IProgressMonitor monitor) {
161
162 // force refresh all the caches before
163 IMetadataRepositoryManager metaManager = ProvUI.getMetadataRepositoryManager(ProvisioningUI.getDefaultUI().getSession());
164 URI[] repos = metaManager.getKnownRepositories(IRepositoryManager.REPOSITORIES_ALL);
165 for(URI repo : repos) {
166 try {
167 metaManager.refreshRepository(repo, monitor);
168 } catch (ProvisionException pe) {
169 IStatus errorStatus = new Status(IStatus.ERROR, TaxonomicEditorPlugin.PLUGIN_ID,
170 "Error occured while reloading cache.", pe);
171
172 } catch (OperationCanceledException oce) {
173 IStatus errorStatus = new Status(IStatus.ERROR, TaxonomicEditorPlugin.PLUGIN_ID,
174 "Error occured while reloading cache.", oce);
175 }
176 }
177 BundleContext bundleContext = TaxonomicEditorPlugin.getContext();
178 ServiceReference reference = bundleContext.getServiceReference(IProvisioningAgent.SERVICE_NAME);
179 if (reference == null) {
180 IStatus errorStatus = new Status(IStatus.ERROR, TaxonomicEditorPlugin.PLUGIN_ID,
181 "No provisioning agent found. This application is not set up for updates.");
182 return errorStatus;
183 }
184
185 final IProvisioningAgent agent = (IProvisioningAgent) bundleContext.getService(reference);
186 IStatus updateStatus;
187 try {
188 updateStatus = P2Util.checkForUpdates(agent, monitor);
189 MessagingUtils.info(updateStatus);
190 } finally {
191 bundleContext.ungetService(reference);
192 }
193 return updateStatus;
194 }
195
196 /**
197 * @param agent
198 * @param monitor
199 * @return
200 * @throws OperationCanceledException
201 */
202 static IStatus checkForUpdates(IProvisioningAgent agent, final IProgressMonitor monitor) {
203 ProvisioningSession session = new ProvisioningSession(agent);
204 // the default update operation looks for updates to the currently
205 // running profile, using the default profile root marker. To change
206 // which installable units are being updated, use the more detailed
207 // constructors.
208 final UpdateOperation operation = new UpdateOperation(session);
209 // try {
210 // setUpdateRepositories(operation);
211 // } catch (URISyntaxException e) {
212 // MessagingUtils.errorDialog("Invalid update site URI",
213 // operation,
214 // "The update site URI has an invalid syntax",
215 // TaxonomicEditorPlugin.PLUGIN_ID,
216 // e,
217 // false);
218 // return null;
219 // }
220
221 final IStatus status = operation.resolveModal(monitor);
222
223 if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
224 return status;
225 }
226
227 if (status.isOK() && status.getSeverity() != IStatus.ERROR) {
228 // We need this block of code to be in async execution
229 // since the confirm dialogs work only on the UI thread
230 Display.getDefault().asyncExec(new Runnable() {
231 @Override
232 public void run() {
233 String updates = "";
234 Update[] possibleUpdates = operation
235 .getPossibleUpdates();
236 for (Update update : possibleUpdates) {
237 updates += update + "\n";
238 }
239
240 boolean doInstall = MessagingUtils.confirmDialog("Updates available", "Do you want to install the available updates ?");
241 // We may need to think whether we still run in async mode once
242 // the user agrees to update. Maybe be reasonable to change to blocking
243 // from this point until the update is complete.
244
245 // More complex status handling might include showing the user what
246 // updates are available if there are multiples, differentiating
247 // patches vs. updates, etc. In this example, we simply update as
248 // suggested by the operation.
249 if(doInstall) {
250 ProvisioningJob provisioningJob = operation.getProvisioningJob(monitor);
251 if (provisioningJob == null) {
252 MessagingUtils.messageDialog("Error in performing update",
253 operation,
254 "ProvisioningJob could not be created." + System.getProperty("line.separator") +
255 "Either this application does not support p2 software installation or this application has been launched from within the Eclipse IDE",
256 null,
257 false);
258
259 } else {
260 // register a job change listener to track
261 // installation progress and notify user upon success
262 provisioningJob
263 .addJobChangeListener(new JobChangeAdapter() {
264 @Override
265 public void done(IJobChangeEvent event) {
266 if (event.getResult().isOK()) {
267 // We need this block of code to be in async execution
268 // since the confirm dialogs work only on the UI thread
269 Display.getDefault().asyncExec(new Runnable() {
270 @Override
271 public void run() {
272 boolean restart = MessagingUtils.confirmDialog(
273 "Updates installed, restart?",
274 "Updates have been installed successfully, do you want to restart?");
275 if (restart) {
276 PlatformUI.getWorkbench().restart();
277 }
278 }
279 });
280 }
281 super.done(event);
282 }
283 });
284 provisioningJob.schedule();
285 }
286 }
287 }
288 });
289 }
290 return status;
291 }
292 }