Replace System.out with proper tracing
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / Activator.java
blob3d18d88e0f32eb3f6e2972a22422e4b39fa85747
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.internal.trace.GitTraceLocation;
33 import org.eclipse.egit.core.project.RepositoryMapping;
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.swt.graphics.Font;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.ui.plugin.AbstractUIPlugin;
45 import org.eclipse.ui.themes.ITheme;
46 import org.osgi.framework.BundleContext;
47 import org.osgi.framework.ServiceReference;
49 /**
50 * This is a plugin singleton mostly controlling logging.
52 public class Activator extends AbstractUIPlugin {
54 /**
55 * The one and only instance
57 private static Activator plugin;
59 /**
60 * Property listeners for plugin specific events
62 private static List<IPropertyChangeListener> propertyChangeListeners =
63 new ArrayList<IPropertyChangeListener>(5);
65 /**
66 * Property constant indicating the decorator configuration has changed.
68 public static final String DECORATORS_CHANGED = "org.eclipse.egit.ui.DECORATORS_CHANGED"; //$NON-NLS-1$
70 /**
71 * @return the {@link Activator} singleton.
73 public static Activator getDefault() {
74 return plugin;
77 /**
78 * @return the id of the egit ui plugin
80 public static String getPluginId() {
81 return getDefault().getBundle().getSymbolicName();
84 /**
85 * Returns the standard display to be used. The method first checks, if the
86 * thread calling this method has an associated display. If so, this display
87 * is returned. Otherwise the method returns the default display.
89 * @return the display to use
91 public static Display getStandardDisplay() {
92 Display display = Display.getCurrent();
93 if (display == null) {
94 display = Display.getDefault();
96 return display;
99 /**
100 * Instantiate an error exception.
102 * @param message
103 * description of the error
104 * @param thr
105 * cause of the error or null
106 * @return an initialized {@link CoreException}
108 public static CoreException error(final String message, final Throwable thr) {
109 return new CoreException(new Status(IStatus.ERROR, getPluginId(), 0,
110 message, thr));
114 * Log an error via the Eclipse logging routines.
116 * @param message
117 * @param thr
118 * cause of error
120 public static void logError(final String message, final Throwable thr) {
121 getDefault().getLog().log(
122 new Status(IStatus.ERROR, getPluginId(), 0, message, thr));
126 * Get the theme used by this plugin.
128 * @return our theme.
130 public static ITheme getTheme() {
131 return plugin.getWorkbench().getThemeManager().getCurrentTheme();
135 * Get a font known to this plugin.
137 * @param id
138 * one of our THEME_* font preference ids (see
139 * {@link UIPreferences});
140 * @return the configured font, borrowed from the registry.
142 public static Font getFont(final String id) {
143 return getTheme().getFontRegistry().get(id);
147 * Get a font known to this plugin, but with bold style applied over top.
149 * @param id
150 * one of our THEME_* font preference ids (see
151 * {@link UIPreferences});
152 * @return the configured font, borrowed from the registry.
154 public static Font getBoldFont(final String id) {
155 return getTheme().getFontRegistry().getBold(id);
158 private RCS rcs;
159 private RIRefresh refreshJob;
162 * Constructor for the egit ui plugin singleton
164 public Activator() {
165 plugin = this;
168 public void start(final BundleContext context) throws Exception {
169 super.start(context);
170 setupSSH(context);
171 setupProxy(context);
172 setupRepoChangeScanner();
173 setupRepoIndexRefresh();
176 private void setupRepoIndexRefresh() {
177 refreshJob = new RIRefresh();
178 Repository.addAnyRepositoryChangedListener(refreshJob);
182 * Register for changes made to Team properties.
184 * @param listener
185 * The listener to register
187 public static synchronized void addPropertyChangeListener(
188 IPropertyChangeListener listener) {
189 propertyChangeListeners.add(listener);
193 * Remove a Team property changes.
195 * @param listener
196 * The listener to remove
198 public static synchronized void removePropertyChangeListener(
199 IPropertyChangeListener listener) {
200 propertyChangeListeners.remove(listener);
204 * Broadcast a Team property change.
206 * @param event
207 * The event to broadcast
209 public static synchronized void broadcastPropertyChange(PropertyChangeEvent event) {
210 for (IPropertyChangeListener listener : propertyChangeListeners)
211 listener.propertyChange(event);
214 static class RIRefresh extends Job implements RepositoryListener {
216 RIRefresh() {
217 super("Git index refresh Job");
220 private Set<IProject> projectsToScan = new LinkedHashSet<IProject>();
222 @Override
223 protected IStatus run(IProgressMonitor monitor) {
224 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
225 monitor.beginTask("Refreshing git managed projects", projects.length);
227 while (projectsToScan.size() > 0) {
228 IProject p;
229 synchronized (projectsToScan) {
230 if (projectsToScan.size() == 0)
231 break;
232 Iterator<IProject> i = projectsToScan.iterator();
233 p = i.next();
234 i.remove();
236 ISchedulingRule rule = p.getWorkspace().getRuleFactory().refreshRule(p);
237 try {
238 getJobManager().beginRule(rule, monitor);
239 p.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 1));
240 } catch (CoreException e) {
241 logError("Failed to refresh projects from index changes", e);
242 return new Status(IStatus.ERROR, getPluginId(), e.getMessage());
243 } finally {
244 getJobManager().endRule(rule);
247 monitor.done();
248 return Status.OK_STATUS;
251 public void indexChanged(IndexChangedEvent e) {
252 // Check the workspace setting "refresh automatically" setting first
253 if (!ResourcesPlugin.getPlugin().getPluginPreferences().getBoolean(
254 ResourcesPlugin.PREF_AUTO_REFRESH))
255 return;
257 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
258 Set<IProject> toRefresh= new HashSet<IProject>();
259 for (IProject p : projects) {
260 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
261 if (mapping != null && mapping.getRepository() == e.getRepository()) {
262 toRefresh.add(p);
265 synchronized (projectsToScan) {
266 projectsToScan.addAll(toRefresh);
268 if (projectsToScan.size() > 0)
269 schedule();
272 public void refsChanged(RefsChangedEvent e) {
273 // Do not react here
278 static class RCS extends Job {
279 RCS() {
280 super("Repository Change Scanner");
283 // FIXME, need to be more intelligent about this to avoid too much work
284 private static final long REPO_SCAN_INTERVAL = 10000L;
286 @Override
287 protected IStatus run(IProgressMonitor monitor) {
288 try {
289 // A repository can contain many projects, only scan once
290 // (a project could in theory be distributed among many
291 // repositories. We discard that as being ugly and stupid for
292 // the moment.
293 IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
294 monitor.beginTask("Scanning Git repositories for changes", projects.length);
295 Set<Repository> scanned = new HashSet<Repository>();
296 for (IProject p : projects) {
297 RepositoryMapping mapping = RepositoryMapping.getMapping(p);
298 if (mapping != null) {
299 Repository r = mapping.getRepository();
300 if (!scanned.contains(r)) {
301 if (monitor.isCanceled())
302 break;
303 // TODO is this the right location?
304 if (GitTraceLocation.UI.isActive())
305 GitTraceLocation.getTrace().trace(
306 GitTraceLocation.UI.getLocation(),
307 "Scanning " + r + " for changes"); //$NON-NLS-1$ //$NON-NLS-2$
308 scanned.add(r);
309 ISchedulingRule rule = p.getWorkspace().getRuleFactory().modifyRule(p);
310 getJobManager().beginRule(rule, monitor);
311 try {
312 r.scanForRepoChanges();
313 } finally {
314 getJobManager().endRule(rule);
318 monitor.worked(1);
320 monitor.done();
321 // TODO is this the right location?
322 if (GitTraceLocation.UI.isActive())
323 GitTraceLocation.getTrace().trace(
324 GitTraceLocation.UI.getLocation(),
325 "Rescheduling " + getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
326 schedule(REPO_SCAN_INTERVAL);
327 } catch (Exception e) {
328 // TODO is this the right location?
329 if (GitTraceLocation.UI.isActive())
330 GitTraceLocation.getTrace().trace(
331 GitTraceLocation.UI.getLocation(),
332 "Stopped rescheduling " + getName() + "job"); //$NON-NLS-1$ //$NON-NLS-2$
333 return new Status(
334 IStatus.ERROR,
335 getPluginId(),
337 "An error occurred while scanning for changes. Scanning aborted",
340 return Status.OK_STATUS;
344 private void setupRepoChangeScanner() {
345 rcs = new RCS();
346 rcs.schedule(RCS.REPO_SCAN_INTERVAL);
349 private void setupSSH(final BundleContext context) {
350 final ServiceReference ssh;
352 ssh = context.getServiceReference(IJSchService.class.getName());
353 if (ssh != null) {
354 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
355 (IJSchService) context.getService(ssh)));
359 private void setupProxy(final BundleContext context) {
360 final ServiceReference proxy;
362 proxy = context.getServiceReference(IProxyService.class.getName());
363 if (proxy != null) {
364 ProxySelector.setDefault(new EclipseProxySelector(
365 (IProxyService) context.getService(proxy)));
366 Authenticator.setDefault(new EclipseAuthenticator(
367 (IProxyService) context.getService(proxy)));
371 public void stop(final BundleContext context) throws Exception {
372 if (GitTraceLocation.UI.isActive())
373 GitTraceLocation.getTrace().trace(
374 GitTraceLocation.UI.getLocation(),
375 "Trying to cancel " + rcs.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
376 rcs.cancel();
377 if (GitTraceLocation.UI.isActive())
378 GitTraceLocation.getTrace().trace(
379 GitTraceLocation.UI.getLocation(),
380 "Trying to cancel " + refreshJob.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
381 refreshJob.cancel();
383 rcs.join();
384 refreshJob.join();
386 if (GitTraceLocation.UI.isActive())
387 GitTraceLocation.getTrace().trace(
388 GitTraceLocation.UI.getLocation(), "Jobs terminated"); //$NON-NLS-1$
389 super.stop(context);
390 plugin = null;