13ecaafddcb8f446724c29be4e815bb979201cbb
[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
6 import org.eclipse.core.runtime.IProgressMonitor;
7 import org.eclipse.core.runtime.IStatus;
8 import org.eclipse.core.runtime.OperationCanceledException;
9 import org.eclipse.core.runtime.Status;
10 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
11 import org.eclipse.core.runtime.jobs.Job;
12 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
13 import org.eclipse.equinox.p2.core.IProvisioningAgent;
14 import org.eclipse.equinox.p2.operations.ProvisioningJob;
15 import org.eclipse.equinox.p2.operations.ProvisioningSession;
16 import org.eclipse.equinox.p2.operations.Update;
17 import org.eclipse.equinox.p2.operations.UpdateOperation;
18 import org.eclipse.swt.widgets.Display;
19 import org.eclipse.ui.PlatformUI;
20 import org.osgi.framework.BundleContext;
21 import org.osgi.framework.ServiceReference;
22
23 import eu.etaxonomy.taxeditor.ApplicationUtil;
24 import eu.etaxonomy.taxeditor.TaxonomicEditorPlugin;
25 import eu.etaxonomy.taxeditor.model.MessagingUtils;
26
27 /**
28 * This class is a utility class for updating the editor from a p2 update site,
29 * greatly inspired by the links given below.
30 *
31 *
32 * To Do :
33 * - Allow configurable update sites
34 *
35 * Notes :
36 * - tried to get rid of the popup dialog which warns about unsigned jars using
37 * -Declipse.p2.unsignedPolicy=allow but this does not work due to
38 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=391399, so the only solution
39 * is to sign the jars with an official certificate.
40 *
41 * @see http://wiki.eclipse.org/Equinox/p2/Adding_Self-Update_to_an_RCP_Application
42 * @see http://bugs.eclipse.org/281226
43 * @see http://www.vogella.com/tutorials/EclipseP2Update/article.html
44 */
45 public class P2Util {
46
47 //private static String LOCAL_UPDATE_SITE = "file:///home/cmathew/Development/EDIT/taxeditor/eu.etaxonomy.taxeditor/target/repository/";
48 private static String EDIT_NIGHTLY_UPDATE_SITE = "http://cybertaxonomy.eu/download/taxeditor/update/nightly/";
49 private static String EDIT_STABLE_UPDATE_SITE = "http://cybertaxonomy.eu/download/taxeditor/update/stable/";
50
51 public static void setUpdateRepositories(UpdateOperation operation) throws URISyntaxException {
52 String updateSite = EDIT_NIGHTLY_UPDATE_SITE;
53 if(ApplicationUtil.isStable()) {
54 updateSite = EDIT_STABLE_UPDATE_SITE;
55 }
56 operation.getProvisioningContext().setMetadataRepositories(new URI[]{new URI(updateSite)});
57 operation.getProvisioningContext().setArtifactRepositories(new URI[]{new URI(updateSite)});
58 }
59 /**
60 *
61 *
62 */
63 public static void checkForUpdates() {
64 // the main job which performs the update
65 Job updateJob = new Job("Update Job") {
66 @Override
67 public IStatus run(IProgressMonitor monitor) {
68 return doCheckForUpdates(monitor);
69 }
70 };
71 updateJob.schedule();
72 }
73
74 /**
75 * @param monitor
76 * @return
77 */
78 private static IStatus doCheckForUpdates(IProgressMonitor monitor) {
79
80 BundleContext bundleContext = TaxonomicEditorPlugin.getContext();
81 ServiceReference reference = bundleContext.getServiceReference(IProvisioningAgent.SERVICE_NAME);
82 if (reference == null) {
83 IStatus errorStatus = new Status(IStatus.ERROR, TaxonomicEditorPlugin.PLUGIN_ID,
84 "No provisioning agent found. This application is not set up for updates.");
85 return errorStatus;
86 }
87
88 final IProvisioningAgent agent = (IProvisioningAgent) bundleContext.getService(reference);
89 IStatus updateStatus;
90 try {
91 updateStatus = P2Util.checkForUpdates(agent, monitor);
92 MessagingUtils.info(updateStatus);
93 } finally {
94 bundleContext.ungetService(reference);
95 }
96 return updateStatus;
97 }
98
99 /**
100 * @param agent
101 * @param monitor
102 * @return
103 * @throws OperationCanceledException
104 */
105 static IStatus checkForUpdates(IProvisioningAgent agent, final IProgressMonitor monitor) {
106 ProvisioningSession session = new ProvisioningSession(agent);
107 // the default update operation looks for updates to the currently
108 // running profile, using the default profile root marker. To change
109 // which installable units are being updated, use the more detailed
110 // constructors.
111 final UpdateOperation operation = new UpdateOperation(session);
112 try {
113 setUpdateRepositories(operation);
114 } catch (URISyntaxException e) {
115 MessagingUtils.errorDialog("Invalid update site URI",
116 operation,
117 "The update site URI has an invalid syntax",
118 TaxonomicEditorPlugin.PLUGIN_ID,
119 e,
120 false);
121 return null;
122 }
123
124 final IStatus status = operation.resolveModal(monitor);
125
126 if (status.getCode() == UpdateOperation.STATUS_NOTHING_TO_UPDATE) {
127 return status;
128 }
129
130 if (status.isOK() && status.getSeverity() != IStatus.ERROR) {
131 // We need this block of code to be in async execution
132 // since the confirm dialogs work only on the UI thread
133 Display.getDefault().asyncExec(new Runnable() {
134 @Override
135 public void run() {
136 String updates = "";
137 Update[] possibleUpdates = operation
138 .getPossibleUpdates();
139 for (Update update : possibleUpdates) {
140 updates += update + "\n";
141 }
142
143 boolean doInstall = MessagingUtils.confirmDialog("Updates available", "Do you want to install the available updates ?");
144 // We may need to think whether we still run in async mode once
145 // the user agrees to update. Maybe be reasonable to change to blocking
146 // from this point until the update is complete.
147
148 // More complex status handling might include showing the user what
149 // updates are available if there are multiples, differentiating
150 // patches vs. updates, etc. In this example, we simply update as
151 // suggested by the operation.
152 if(doInstall) {
153 ProvisioningJob provisioningJob = operation.getProvisioningJob(monitor);
154 if (provisioningJob == null) {
155 MessagingUtils.messageDialog("Error in performing update",
156 operation,
157 "ProvisioningJob could not be created." + System.getProperty("line.separator") +
158 "Either this application does not support p2 software installation or this application has been launched from within the Eclipse IDE",
159 null,
160 false);
161
162 } else {
163 // register a job change listener to track
164 // installation progress and notify user upon success
165 provisioningJob
166 .addJobChangeListener(new JobChangeAdapter() {
167 @Override
168 public void done(IJobChangeEvent event) {
169 if (event.getResult().isOK()) {
170 // We need this block of code to be in async execution
171 // since the confirm dialogs work only on the UI thread
172 Display.getDefault().asyncExec(new Runnable() {
173 @Override
174 public void run() {
175 boolean restart = MessagingUtils.confirmDialog(
176 "Updates installed, restart?",
177 "Updates have been installed successfully, do you want to restart?");
178 if (restart) {
179 PlatformUI.getWorkbench().restart();
180 }
181 }
182 });
183 }
184 super.done(event);
185 }
186 });
187 provisioningJob.schedule();
188 }
189 }
190 }
191 });
192 }
193 return status;
194 }
195 }