Fix error handling in some actions
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / Activator.java
blobec025bcf8be1fb773587dea5ba69b9ff9053932d
1 /*******************************************************************************
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 *******************************************************************************/
10 package org.eclipse.egit.ui;
12 import java.net.Authenticator;
13 import java.net.ProxySelector;
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.Iterator;
17 import java.util.LinkedHashSet;
18 import java.util.List;
19 import java.util.Set;
21 import org.eclipse.core.net.proxy.IProxyService;
22 import org.eclipse.core.resources.IProject;
23 import org.eclipse.core.resources.IResource;
24 import org.eclipse.core.resources.ResourcesPlugin;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.Status;
29 import org.eclipse.core.runtime.SubProgressMonitor;
30 import org.eclipse.core.runtime.jobs.ISchedulingRule;
31 import org.eclipse.core.runtime.jobs.Job;
32 import org.eclipse.egit.core.project.RepositoryMapping;
33 import org.eclipse.egit.ui.internal.trace.GitTraceLocation;
34 import org.eclipse.jface.util.IPropertyChangeListener;
35 import org.eclipse.jface.util.PropertyChangeEvent;
36 import org.eclipse.jgit.lib.IndexChangedEvent;
37 import org.eclipse.jgit.lib.RefsChangedEvent;
38 import org.eclipse.jgit.lib.Repository;
39 import org.eclipse.jgit.lib.RepositoryListener;
40 import org.eclipse.jgit.transport.SshSessionFactory;
41 import org.eclipse.jsch.core.IJSchService;
42 import org.eclipse.osgi.service.debug.DebugOptions;
43 import org.eclipse.swt.graphics.Font;
44 import org.eclipse.ui.plugin.AbstractUIPlugin;
45 import org.eclipse.ui.statushandlers.StatusManager;
46 import org.eclipse.ui.themes.ITheme;
47 import org.osgi.framework.BundleContext;
48 import org.osgi.framework.ServiceReference;
49 import org.osgi.util.tracker.ServiceTracker;
51 /**
52 * This is a plugin singleton mostly controlling logging.
54 public class Activator extends AbstractUIPlugin {
56 /**
57 * The one and only instance
59 private static Activator plugin;
61 /**
62 * Property listeners for plugin specific events
64 private static List<IPropertyChangeListener> propertyChangeListeners =
65 new ArrayList<IPropertyChangeListener>(5);
67 /**
68 * Property constant indicating the decorator configuration has changed.
70 public static final String DECORATORS_CHANGED = "org.eclipse.egit.ui.DECORATORS_CHANGED"; //$NON-NLS-1$
72 /**
73 * @return the {@link Activator} singleton.
75 public static Activator getDefault() {
76 return plugin;
79 /**
80 * @return the id of the egit ui plugin
82 public static String getPluginId() {
83 return getDefault().getBundle().getSymbolicName();
86 /**
87 * Handle an error. The error is logged. If <code>show</code> is
88 * <code>true</code> the error is shown to the user.
90 * @param message a localized message
91 * @param throwable
92 * @param show
94 public static void handleError(String message, Throwable throwable,
95 boolean show) {
96 IStatus status = new Status(IStatus.ERROR, getPluginId(), message,
97 throwable);
98 int style = StatusManager.LOG;
99 if (show)
100 style |= StatusManager.SHOW;
101 StatusManager.getManager().handle(status, style);
105 * Get the theme used by this plugin.
107 * @return our theme.
109 public static ITheme getTheme() {
110 return plugin.getWorkbench().getThemeManager().getCurrentTheme();
114 * Get a font known to this plugin.
116 * @param id
117 * one of our THEME_* font preference ids (see
118 * {@link UIPreferences});
119 * @return the configured font, borrowed from the registry.
121 public static Font getFont(final String id) {
122 return getTheme().getFontRegistry().get(id);
126 * Get a font known to this plugin, but with bold style applied over top.
128 * @param id
129 * one of our THEME_* font preference ids (see
130 * {@link UIPreferences});
131 * @return the configured font, borrowed from the registry.
133 public static Font getBoldFont(final String id) {
134 return getTheme().getFontRegistry().getBold(id);
137 private RCS rcs;
138 private RIRefresh refreshJob;
141 * Constructor for the egit ui plugin singleton
143 public Activator() {
144 plugin = this;
147 public void start(final BundleContext context) throws Exception {
148 super.start(context);
150 if (isDebugging()) {
151 ServiceTracker debugTracker = new ServiceTracker(context,
152 DebugOptions.class.getName(), null);
153 debugTracker.open();
155 DebugOptions opts = (DebugOptions) debugTracker.getService();
156 GitTraceLocation.initializeFromOptions(opts, true);
159 setupSSH(context);
160 setupProxy(context);
161 setupRepoChangeScanner();
162 setupRepoIndexRefresh();
165 private void setupRepoIndexRefresh() {
166 refreshJob = new RIRefresh();
167 Repository.addAnyRepositoryChangedListener(refreshJob);
171 * Register for changes made to Team properties.
173 * @param listener
174 * The listener to register
176 public static synchronized void addPropertyChangeListener(
177 IPropertyChangeListener listener) {
178 propertyChangeListeners.add(listener);
182 * Remove a Team property changes.
184 * @param listener
185 * The listener to remove
187 public static synchronized void removePropertyChangeListener(
188 IPropertyChangeListener listener) {
189 propertyChangeListeners.remove(listener);
193 * Broadcast a Team property change.
195 * @param event
196 * The event to broadcast
198 public static synchronized void broadcastPropertyChange(PropertyChangeEvent event) {
199 for (IPropertyChangeListener listener : propertyChangeListeners)
200 listener.propertyChange(event);
203 static class RIRefresh extends Job implements RepositoryListener {
205 RIRefresh() {
206 super(UIText.Activator_refreshJobName);
209 private Set<IProject> projectsToScan = new LinkedHashSet<IProject>();
211 @Override
212 protected IStatus run(IProgressMonitor monitor) {
213 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
214 monitor.beginTask(UIText.Activator_refreshingProjects, projects.length);
216 while (projectsToScan.size() > 0) {
217 IProject p;
218 synchronized (projectsToScan) {
219 if (projectsToScan.size() == 0)
220 break;
221 Iterator<IProject> i = projectsToScan.iterator();
222 p = i.next();
223 i.remove();
225 ISchedulingRule rule = p.getWorkspace().getRuleFactory().refreshRule(p);
226 try {
227 getJobManager().beginRule(rule, monitor);
228 p.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 1));
229 } catch (CoreException e) {
230 handleError(UIText.Activator_refreshFailed, e, false);
231 return new Status(IStatus.ERROR, getPluginId(), e.getMessage());
232 } finally {
233 getJobManager().endRule(rule);
236 monitor.done();
237 return Status.OK_STATUS;
240 public void indexChanged(IndexChangedEvent e) {
241 // Check the workspace setting "refresh automatically" setting first
242 if (!ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(
243 ResourcesPlugin.PREF_AUTO_REFRESH))
244 return;
246 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
247 Set<IProject> toRefresh= new HashSet<IProject>();
248 for (IProject p : projects) {
249 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
250 if (mapping != null && mapping.getRepository() == e.getRepository()) {
251 toRefresh.add(p);
254 synchronized (projectsToScan) {
255 projectsToScan.addAll(toRefresh);
257 if (projectsToScan.size() > 0)
258 schedule();
261 public void refsChanged(RefsChangedEvent e) {
262 // Do not react here
267 static class RCS extends Job {
268 RCS() {
269 super(UIText.Activator_repoScanJobName);
272 // FIXME, need to be more intelligent about this to avoid too much work
273 private static final long REPO_SCAN_INTERVAL = 10000L;
274 // volatile in order to ensure thread synchronization
275 private volatile boolean doReschedule = true;
277 void setReschedule(boolean reschedule){
278 doReschedule = reschedule;
281 @Override
282 protected IStatus run(IProgressMonitor monitor) {
283 try {
284 // A repository can contain many projects, only scan once
285 // (a project could in theory be distributed among many
286 // repositories. We discard that as being ugly and stupid for
287 // the moment.
288 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
289 monitor.beginTask(UIText.Activator_scanningRepositories, projects.length);
290 Set<Repository> scanned = new HashSet<Repository>();
291 for (IProject p : projects) {
292 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
293 if (mapping != null) {
294 Repository r = mapping.getRepository();
295 if (!scanned.contains(r)) {
296 if (monitor.isCanceled())
297 break;
298 // TODO is this the right location?
299 if (GitTraceLocation.UI.isActive())
300 GitTraceLocation.getTrace().trace(
301 GitTraceLocation.UI.getLocation(),
302 "Scanning " + r + " for changes"); //$NON-NLS-1$ //$NON-NLS-2$
303 scanned.add(r);
304 ISchedulingRule rule = p.getWorkspace().getRuleFactory().modifyRule(p);
305 getJobManager().beginRule(rule, monitor);
306 try {
307 r.scanForRepoChanges();
308 } finally {
309 getJobManager().endRule(rule);
313 monitor.worked(1);
315 monitor.done();
316 // TODO is this the right location?
317 if (GitTraceLocation.UI.isActive())
318 GitTraceLocation.getTrace().trace(
319 GitTraceLocation.UI.getLocation(),
320 "Rescheduling " + getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
321 if (doReschedule)
322 schedule(REPO_SCAN_INTERVAL);
323 } catch (Exception e) {
324 // TODO is this the right location?
325 if (GitTraceLocation.UI.isActive())
326 GitTraceLocation.getTrace().trace(
327 GitTraceLocation.UI.getLocation(),
328 "Stopped rescheduling " + getName() + "job"); //$NON-NLS-1$ //$NON-NLS-2$
329 return new Status(
330 IStatus.ERROR,
331 getPluginId(),
333 UIText.Activator_scanError,
336 return Status.OK_STATUS;
340 private void setupRepoChangeScanner() {
341 rcs = new RCS();
342 rcs.setSystem(true);
343 rcs.schedule(RCS.REPO_SCAN_INTERVAL);
346 private void setupSSH(final BundleContext context) {
347 final ServiceReference ssh;
349 ssh = context.getServiceReference(IJSchService.class.getName());
350 if (ssh != null) {
351 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
352 (IJSchService) context.getService(ssh)));
356 private void setupProxy(final BundleContext context) {
357 final ServiceReference proxy;
359 proxy = context.getServiceReference(IProxyService.class.getName());
360 if (proxy != null) {
361 ProxySelector.setDefault(new EclipseProxySelector(
362 (IProxyService) context.getService(proxy)));
363 Authenticator.setDefault(new EclipseAuthenticator(
364 (IProxyService) context.getService(proxy)));
368 public void stop(final BundleContext context) throws Exception {
370 if (GitTraceLocation.UI.isActive())
371 GitTraceLocation.getTrace().trace(
372 GitTraceLocation.UI.getLocation(),
373 "Trying to cancel " + rcs.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
375 rcs.setReschedule(false);
377 rcs.cancel();
378 if (GitTraceLocation.UI.isActive())
379 GitTraceLocation.getTrace().trace(
380 GitTraceLocation.UI.getLocation(),
381 "Trying to cancel " + refreshJob.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
382 refreshJob.cancel();
384 rcs.join();
385 refreshJob.join();
387 if (GitTraceLocation.UI.isActive())
388 GitTraceLocation.getTrace().trace(
389 GitTraceLocation.UI.getLocation(), "Jobs terminated"); //$NON-NLS-1$
391 super.stop(context);
392 plugin = null;