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