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.ApplicationUtil;
|
34
|
import eu.etaxonomy.taxeditor.TaxonomicEditorPlugin;
|
35
|
import eu.etaxonomy.taxeditor.model.MessagingUtils;
|
36
|
import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
|
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
|
}
|