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
;
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
;
52 * This is a plugin singleton mostly controlling logging.
54 public class Activator
extends AbstractUIPlugin
{
57 * The one and only instance
59 private static Activator plugin
;
62 * Property listeners for plugin specific events
64 private static List
<IPropertyChangeListener
> propertyChangeListeners
=
65 new ArrayList
<IPropertyChangeListener
>(5);
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$
73 * @return the {@link Activator} singleton.
75 public static Activator
getDefault() {
80 * @return the id of the egit ui plugin
82 public static String
getPluginId() {
83 return getDefault().getBundle().getSymbolicName();
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
94 public static void handleError(String message
, Throwable throwable
,
96 IStatus status
= new Status(IStatus
.ERROR
, getPluginId(), message
,
98 int style
= StatusManager
.LOG
;
100 style
|= StatusManager
.SHOW
;
101 StatusManager
.getManager().handle(status
, style
);
105 * Shows an error. The error is NOT logged.
108 * a localized message
111 public static void showError(String message
, Throwable throwable
) {
112 IStatus status
= new Status(IStatus
.ERROR
, getPluginId(), message
,
114 StatusManager
.getManager().handle(status
, StatusManager
.SHOW
);
118 * Get the theme used by this plugin.
122 public static ITheme
getTheme() {
123 return plugin
.getWorkbench().getThemeManager().getCurrentTheme();
127 * Get a font known to this plugin.
130 * one of our THEME_* font preference ids (see
131 * {@link UIPreferences});
132 * @return the configured font, borrowed from the registry.
134 public static Font
getFont(final String id
) {
135 return getTheme().getFontRegistry().get(id
);
139 * Get a font known to this plugin, but with bold style applied over top.
142 * one of our THEME_* font preference ids (see
143 * {@link UIPreferences});
144 * @return the configured font, borrowed from the registry.
146 public static Font
getBoldFont(final String id
) {
147 return getTheme().getFontRegistry().getBold(id
);
151 private RIRefresh refreshJob
;
154 * Constructor for the egit ui plugin singleton
160 public void start(final BundleContext context
) throws Exception
{
161 super.start(context
);
164 ServiceTracker debugTracker
= new ServiceTracker(context
,
165 DebugOptions
.class.getName(), null);
168 DebugOptions opts
= (DebugOptions
) debugTracker
.getService();
169 GitTraceLocation
.initializeFromOptions(opts
, true);
174 setupRepoChangeScanner();
175 setupRepoIndexRefresh();
178 private void setupRepoIndexRefresh() {
179 refreshJob
= new RIRefresh();
180 Repository
.addAnyRepositoryChangedListener(refreshJob
);
184 * Register for changes made to Team properties.
187 * The listener to register
189 public static synchronized void addPropertyChangeListener(
190 IPropertyChangeListener listener
) {
191 propertyChangeListeners
.add(listener
);
195 * Remove a Team property changes.
198 * The listener to remove
200 public static synchronized void removePropertyChangeListener(
201 IPropertyChangeListener listener
) {
202 propertyChangeListeners
.remove(listener
);
206 * Broadcast a Team property change.
209 * The event to broadcast
211 public static synchronized void broadcastPropertyChange(PropertyChangeEvent event
) {
212 for (IPropertyChangeListener listener
: propertyChangeListeners
)
213 listener
.propertyChange(event
);
216 static class RIRefresh
extends Job
implements RepositoryListener
{
219 super(UIText
.Activator_refreshJobName
);
222 private Set
<IProject
> projectsToScan
= new LinkedHashSet
<IProject
>();
225 protected IStatus
run(IProgressMonitor monitor
) {
226 IProject
[] projects
= ResourcesPlugin
.getWorkspace().getRoot().getProjects();
227 monitor
.beginTask(UIText
.Activator_refreshingProjects
, projects
.length
);
229 while (projectsToScan
.size() > 0) {
231 synchronized (projectsToScan
) {
232 if (projectsToScan
.size() == 0)
234 Iterator
<IProject
> i
= projectsToScan
.iterator();
238 ISchedulingRule rule
= p
.getWorkspace().getRuleFactory().refreshRule(p
);
240 getJobManager().beginRule(rule
, monitor
);
241 p
.refreshLocal(IResource
.DEPTH_INFINITE
, new SubProgressMonitor(monitor
, 1));
242 } catch (CoreException e
) {
243 handleError(UIText
.Activator_refreshFailed
, e
, false);
244 return new Status(IStatus
.ERROR
, getPluginId(), e
.getMessage());
246 getJobManager().endRule(rule
);
250 return Status
.OK_STATUS
;
253 public void indexChanged(IndexChangedEvent e
) {
254 // Check the workspace setting "refresh automatically" setting first
255 if (!ResourcesPlugin
.getPlugin().getPluginPreferences().getBoolean(
256 ResourcesPlugin
.PREF_AUTO_REFRESH
))
259 IProject
[] projects
= ResourcesPlugin
.getWorkspace().getRoot().getProjects();
260 Set
<IProject
> toRefresh
= new HashSet
<IProject
>();
261 for (IProject p
: projects
) {
262 RepositoryMapping mapping
= RepositoryMapping
.getMapping(p
);
263 if (mapping
!= null && mapping
.getRepository() == e
.getRepository()) {
267 synchronized (projectsToScan
) {
268 projectsToScan
.addAll(toRefresh
);
270 if (projectsToScan
.size() > 0)
274 public void refsChanged(RefsChangedEvent e
) {
280 static class RCS
extends Job
{
282 super(UIText
.Activator_repoScanJobName
);
285 // FIXME, need to be more intelligent about this to avoid too much work
286 private static final long REPO_SCAN_INTERVAL
= 10000L;
287 // volatile in order to ensure thread synchronization
288 private volatile boolean doReschedule
= true;
290 void setReschedule(boolean reschedule
){
291 doReschedule
= reschedule
;
295 protected IStatus
run(IProgressMonitor monitor
) {
297 // A repository can contain many projects, only scan once
298 // (a project could in theory be distributed among many
299 // repositories. We discard that as being ugly and stupid for
301 IProject
[] projects
= ResourcesPlugin
.getWorkspace().getRoot().getProjects();
302 monitor
.beginTask(UIText
.Activator_scanningRepositories
, projects
.length
);
303 Set
<Repository
> scanned
= new HashSet
<Repository
>();
304 for (IProject p
: projects
) {
305 RepositoryMapping mapping
= RepositoryMapping
.getMapping(p
);
306 if (mapping
!= null) {
307 Repository r
= mapping
.getRepository();
308 if (!scanned
.contains(r
)) {
309 if (monitor
.isCanceled())
311 // TODO is this the right location?
312 if (GitTraceLocation
.UI
.isActive())
313 GitTraceLocation
.getTrace().trace(
314 GitTraceLocation
.UI
.getLocation(),
315 "Scanning " + r
+ " for changes"); //$NON-NLS-1$ //$NON-NLS-2$
317 ISchedulingRule rule
= p
.getWorkspace().getRuleFactory().modifyRule(p
);
318 getJobManager().beginRule(rule
, monitor
);
320 r
.scanForRepoChanges();
322 getJobManager().endRule(rule
);
329 // TODO is this the right location?
330 if (GitTraceLocation
.UI
.isActive())
331 GitTraceLocation
.getTrace().trace(
332 GitTraceLocation
.UI
.getLocation(),
333 "Rescheduling " + getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
335 schedule(REPO_SCAN_INTERVAL
);
336 } catch (Exception e
) {
337 // TODO is this the right location?
338 if (GitTraceLocation
.UI
.isActive())
339 GitTraceLocation
.getTrace().trace(
340 GitTraceLocation
.UI
.getLocation(),
341 "Stopped rescheduling " + getName() + "job"); //$NON-NLS-1$ //$NON-NLS-2$
346 UIText
.Activator_scanError
,
349 return Status
.OK_STATUS
;
353 private void setupRepoChangeScanner() {
356 rcs
.schedule(RCS
.REPO_SCAN_INTERVAL
);
359 private void setupSSH(final BundleContext context
) {
360 final ServiceReference ssh
;
362 ssh
= context
.getServiceReference(IJSchService
.class.getName());
364 SshSessionFactory
.setInstance(new EclipseSshSessionFactory(
365 (IJSchService
) context
.getService(ssh
)));
369 private void setupProxy(final BundleContext context
) {
370 final ServiceReference proxy
;
372 proxy
= context
.getServiceReference(IProxyService
.class.getName());
374 ProxySelector
.setDefault(new EclipseProxySelector(
375 (IProxyService
) context
.getService(proxy
)));
376 Authenticator
.setDefault(new EclipseAuthenticator(
377 (IProxyService
) context
.getService(proxy
)));
381 public void stop(final BundleContext context
) throws Exception
{
383 if (GitTraceLocation
.UI
.isActive())
384 GitTraceLocation
.getTrace().trace(
385 GitTraceLocation
.UI
.getLocation(),
386 "Trying to cancel " + rcs
.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
388 rcs
.setReschedule(false);
391 if (GitTraceLocation
.UI
.isActive())
392 GitTraceLocation
.getTrace().trace(
393 GitTraceLocation
.UI
.getLocation(),
394 "Trying to cancel " + refreshJob
.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
400 if (GitTraceLocation
.UI
.isActive())
401 GitTraceLocation
.getTrace().trace(
402 GitTraceLocation
.UI
.getLocation(), "Jobs terminated"); //$NON-NLS-1$
412 public static void logError(String message
, Throwable e
) {
413 handleError(message
, e
, false);
420 public static void error(String message
, Throwable e
) {
421 handleError(message
, e
, false);
425 * Creates an error status
428 * a localized message
430 * @return a new Status object
432 public static IStatus
createErrorStatus(String message
, Throwable throwable
) {
433 return new Status(IStatus
.ERROR
, getPluginId(), message
, throwable
);