diff options
2 files changed, 160 insertions, 246 deletions
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/BackgroundEventHandler.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/BackgroundEventHandler.java index cb06b607e..378396c44 100644 --- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/BackgroundEventHandler.java +++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/BackgroundEventHandler.java @@ -14,12 +14,12 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; -import org.eclipse.team.core.TeamException; import org.eclipse.team.internal.core.ExceptionCollector; import org.eclipse.team.internal.core.Policy; import org.eclipse.team.internal.core.TeamPlugin; @@ -35,14 +35,14 @@ public abstract class BackgroundEventHandler { // The job that runs when events need to be processed private Job eventHandlerJob; - // Indicate if the event handler has benn shutdown + // Indicate if the event handler has been shutdown private boolean shutdown; - // manages exceptions + // Accumulate exceptions that occur private ExceptionCollector errors; /** - * Internal resource synchronization event. Can contain a result. + * Resource event class. The type is specific to subclasses. */ public static class Event { IResource resource; @@ -73,25 +73,11 @@ public abstract class BackgroundEventHandler { IStatus.ERROR, null /* don't log */ ); - } - - /** - * Create the event handling job and schedule it - */ - protected void initializeEventHandlingJob() { createEventHandlingJob(); schedule(); } /** - * Handle the exception by recording it in the errors list. - * @param e - */ - protected void handleException(TeamException e) { - errors.handleException(e); - - } - /** * Create the job used for processing the events in the queue. The job stops working when * the queue is empty. */ @@ -100,16 +86,46 @@ public abstract class BackgroundEventHandler { public IStatus run(IProgressMonitor monitor) { return processEvents(monitor); } + public boolean shouldRun() { + return hasUnprocessedEvents(); + } + public boolean shouldSchedule() { + return hasUnprocessedEvents(); + } }; eventHandlerJob.addJobChangeListener(new JobChangeAdapter() { public void done(IJobChangeEvent event) { - jobDone(); + jobDone(event); } }); eventHandlerJob.setPriority(Job.SHORT); } /** + * This method is invoked when the processing job completes. The + * default behavior of the handler is to restart the job if the queue + * is no longer empty and to clear the queue if the handler was shut down. + */ + protected void jobDone(IJobChangeEvent event) { + if (isShutdown()) { + // The handler has been shutdown. Clean up the queue. + synchronized(this) { + awaitingProcessing.clear(); + } + } else if (hasUnprocessedEvents()) { + // An event squeaked in as the job was finishing. Reschedule the job. + schedule(); + } + } + + /** + * Schedule the job to process the events now. + */ + protected void schedule() { + eventHandlerJob.schedule(); + } + + /** * Return the name of the handler, which is used as the job name. * @return the name of the handler */ @@ -119,15 +135,7 @@ public abstract class BackgroundEventHandler { * Return the text to be displayed as the title for any errors that occur. * @return */ - public abstract String getErrorsTitle(); - - /** - * Process the event in the context of a background job. - * - * @param event - * @param monitor - */ - protected abstract void processEvent(Event event, IProgressMonitor monitor) throws TeamException; + protected abstract String getErrorsTitle(); /** * Shutdown the event handler. Any events on the queue will be removed from the queue @@ -139,15 +147,20 @@ public abstract class BackgroundEventHandler { } /** + * @return Returns whether the handle has been shutdown. + */ + public boolean isShutdown() { + return shutdown; + } + + /** * Queue the event and start the job if it's not already doing work. */ protected synchronized void queueEvent(Event event) { awaitingProcessing.add(event); - if (shutdown - || eventHandlerJob == null - || eventHandlerJob.getState() != Job.NONE) - return; - else { + if (!isShutdown() + && eventHandlerJob != null + && eventHandlerJob.getState() == Job.NONE) { schedule(); } } @@ -157,17 +170,27 @@ public abstract class BackgroundEventHandler { * @return Event to be processed */ private synchronized Event nextElement() { - if (shutdown || awaitingProcessing.isEmpty()) { + if (isShutdown() || !hasUnprocessedEvents()) { return null; } return (Event) awaitingProcessing.remove(0); } /** - * Process events from the events queue and dispatch results. + * Return whether there are unprocessed events on the event queue. + * @return whether there are unprocessed events on the queue + */ + protected synchronized boolean hasUnprocessedEvents() { + return !awaitingProcessing.isEmpty(); + } + + /** + * Process events from the events queue and dispatch results. This method does not + * directly check for or handle cancelation of the provided monitor. However, + * it does invoke <code>processEvent(Event)</code> which may check for and handle + * cancelation by shuting down the receiver. */ protected IStatus processEvents(IProgressMonitor monitor) { - Event event; errors.clear(); try { // It's hard to know how much work is going to happen @@ -177,12 +200,11 @@ public abstract class BackgroundEventHandler { IProgressMonitor subMonitor = Policy.infiniteSubMonitorFor(monitor, 90); subMonitor.beginTask(null, 1024); - while ((event = nextElement()) != null && ! shutdown) { - // Cancellation is dangerous because this will leave the sync info in a bad state. - // Purposely not checking - + Event event; + while ((event = nextElement()) != null && ! isShutdown()) { try { processEvent(event, subMonitor); - } catch (TeamException e) { + } catch (CoreException e) { // handle exception but keep going handleException(e); } @@ -194,31 +216,23 @@ public abstract class BackgroundEventHandler { } /** - * This method is invoked when the processing job completes. The - * default behavior of the handler is to restart the job if the queue - * is no longer empty and to clear the queue if the handler was shut down. - * + * Handle the exception by recording it in the errors list. + * @param e */ - protected void jobDone() { - // Make sure an unhandled event didn't squeak in unless we are shutdown - if (shutdown == false && hasUnprocessedEvents()) { - schedule(); - } else { - synchronized(this) { - awaitingProcessing.clear(); - } - } - } - protected boolean hasUnprocessedEvents() { - return !awaitingProcessing.isEmpty(); + protected void handleException(CoreException e) { + errors.handleException(e); + } /** - * Schedule the job or process the events now. + * Process the event in the context of a running background job. Subclasses may + * (but are not required to) check the provided monitor for cancelation and shut down the + * receiver by invoking the <code>shutdown()</code> method. + * + * @param event + * @param monitor */ - protected void schedule() { - eventHandlerJob.schedule(); - } + protected abstract void processEvent(Event event, IProgressMonitor monitor) throws CoreException; /** * @return Returns the eventHandlerJob. @@ -226,5 +240,4 @@ public abstract class BackgroundEventHandler { public Job getEventHandlerJob() { return eventHandlerJob; } - } diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SubscriberEventHandler.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SubscriberEventHandler.java index 43bfe6658..badc3430d 100644 --- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SubscriberEventHandler.java +++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/sync/sets/SubscriberEventHandler.java @@ -16,15 +16,10 @@ import java.util.List; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.jobs.IJobChangeEvent; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.team.core.TeamException; +import org.eclipse.team.core.subscribers.BackgroundEventHandler; import org.eclipse.team.core.subscribers.SyncInfo; -import org.eclipse.team.internal.core.ExceptionCollector; import org.eclipse.team.internal.ui.Policy; -import org.eclipse.team.internal.ui.TeamUIPlugin; import org.eclipse.team.internal.ui.jobs.JobStatusHandler; import org.eclipse.team.ui.sync.SubscriberAction; @@ -38,7 +33,7 @@ import org.eclipse.team.ui.sync.SubscriberAction; * OPTIMIZATION: look into provinding events with multiple resources instead of * one. */ -public class SubscriberEventHandler { +public class SubscriberEventHandler extends BackgroundEventHandler { // The number of events to process before feeding into the set. private static final int NOTIFICATION_BATCHING_NUMBER = 10; @@ -46,22 +41,13 @@ public class SubscriberEventHandler { // has been calculated by the job. private SyncSetInputFromSubscriber set; - // Events that need to be processed - private List awaitingProcessing = new ArrayList(); - - // Use to shutdown the job - private boolean shutdown = false; - - // The job that runs when events need to be processed - Job eventHandlerJob; - - // manages exceptions - private ExceptionCollector errors; - + // Changes accumulated by the event handler + private List resultCache = new ArrayList(); + /** * Internal resource synchronization event. Can contain a result. */ - class Event { + class SubscriberEvent extends Event{ static final int REMOVAL = 1; static final int CHANGE = 2; static final int INITIALIZE = 3; @@ -70,27 +56,16 @@ public class SubscriberEventHandler { int depth; SyncInfo result; - Event(IResource resource, int type, int depth) { - this.resource = resource; - this.type = type; - this.depth = depth; + SubscriberEvent(IResource resource, int type, int depth) { + super(resource, type, depth); } - public Event( + public SubscriberEvent( IResource resource, int type, int depth, SyncInfo result) { - this(resource, type, depth); - this.result = result; - } - public int getDepth() { - return depth; - } - public IResource getResource() { - return resource; - } - public int getType() { - return type; + this(resource, type, depth); + this.result = result; } public SyncInfo getResult() { return result; @@ -104,23 +79,16 @@ public class SubscriberEventHandler { */ public SubscriberEventHandler(SyncSetInputFromSubscriber set) { this.set = set; - errors = - new ExceptionCollector( - Policy.bind("SubscriberEventHandler.errors"), //$NON-NLS-1$ - TeamUIPlugin.ID, - IStatus.ERROR, - null /* don't log */ - ); //$NON-NLS-1$ - reset(Event.INITIALIZE); - createEventHandlingJob(); - schedule(); + reset(SubscriberEvent.INITIALIZE); } + /** * Schedule the job or process the events now. */ public void schedule() { - JobStatusHandler.schedule(eventHandlerJob, SubscriberAction.SUBSCRIBER_JOB_TYPE); + JobStatusHandler.schedule(getEventHandlerJob(), SubscriberAction.SUBSCRIBER_JOB_TYPE); } + /** * Initialize all resources for the subscriber associated with the set. This will basically recalculate * all synchronization information for the subscriber. @@ -128,8 +96,9 @@ public class SubscriberEventHandler { * @param depth */ public void initialize() { - reset(Event.CHANGE); + reset(SubscriberEvent.CHANGE); } + /** * Called by a client to indicate that a resource has changed and its synchronization state * should be recalculated. @@ -137,8 +106,9 @@ public class SubscriberEventHandler { * @param depth the depth of the change calculation */ public void change(IResource resource, int depth) { - queueEvent(new Event(resource, Event.CHANGE, depth)); + queueEvent(new SubscriberEvent(resource, SubscriberEvent.CHANGE, depth)); } + /** * Called by a client to indicate that a resource has been removed and should be removed. The * removal will propagate to the set. @@ -146,132 +116,10 @@ public class SubscriberEventHandler { */ public void remove(IResource resource) { queueEvent( - new Event(resource, Event.REMOVAL, IResource.DEPTH_INFINITE)); - } - /** - * Queue the event and start the job if it's not already doing work. - */ - synchronized private void queueEvent(Event event) { - awaitingProcessing.add(event); - if (shutdown - || eventHandlerJob == null - || eventHandlerJob.getState() != Job.NONE) - return; - else { - schedule(); - } - } - /** - * Returns the events handler job. - * @return the job that calculates the synchronization state for a subscriber - */ - public Job getEventHandlerJob() { - return eventHandlerJob; - } - /** - * Shutdown the event handler. - */ - void shutdown() { - shutdown = true; - eventHandlerJob.cancel(); - } - /** - * Get the next resource to be calculated. - * @return Event to be processed - */ - synchronized Event nextElement() { - if (shutdown || awaitingProcessing.isEmpty()) { - return null; - } - return (Event) awaitingProcessing.remove(0); - } - /** - * Create the job used for processing the events in the queue. The job stops working when - * the queue is empty. - */ - private void createEventHandlingJob() { - eventHandlerJob = new Job(Policy.bind("SubscriberEventHandler.jobName")) {//$NON-NLS-1$ - public IStatus run(IProgressMonitor monitor) { - return processEvents(monitor); - } - }; - eventHandlerJob.addJobChangeListener(new JobChangeAdapter() { - public void done(IJobChangeEvent event) { - // Make sure an unhandled event didn't squeak in unless we are shutdown - if (shutdown == false && hasUnprocessedEvents()) { - schedule(); - } else { - synchronized(this) { - awaitingProcessing.clear(); - } - } - } - }); - eventHandlerJob.setPriority(Job.SHORT); + new SubscriberEvent(resource, SubscriberEvent.REMOVAL, IResource.DEPTH_INFINITE)); } /** - * Process events from the events queue and dispatch results. - */ - /* internal use only */ IStatus processEvents(IProgressMonitor monitor) { - List resultCache = new ArrayList(); - Event event; - errors.clear(); - try { - // It's hard to know how much work is going to happen - // since the queue can grow. Use the current queue size as a hint to - // an infinite progress monitor - monitor.beginTask(null, 100); - IProgressMonitor subMonitor = Policy.subInfiniteMonitorFor(monitor, 90); - subMonitor.beginTask(null, 1024); - - while ((event = nextElement()) != null && ! shutdown) { - // Cancellation is dangerous because this will leave the sync info in a bad state. - // Purposely not checking - - try { - int type = event.getType(); - switch (type) { - case Event.REMOVAL : - resultCache.add(event); - break; - case Event.CHANGE : - List results = new ArrayList(); - collect( - event.getResource(), - event.getDepth(), - subMonitor, - results); - resultCache.addAll(results); - break; - case Event.INITIALIZE : - monitor.subTask(Policy.bind("SubscriberEventHandler.2", event.getResource().getFullPath().toString())); //$NON-NLS-1$ - Event[] events = - getAllOutOfSync( - new IResource[] { event.getResource()}, - event.getDepth(), - Policy.subMonitorFor(subMonitor, 64)); - resultCache.addAll(Arrays.asList(events)); - break; - } - } catch (TeamException e) { - // handle exception but keep going - errors.handleException(e); - } - - if (!hasUnprocessedEvents() - || resultCache.size() > NOTIFICATION_BATCHING_NUMBER) { - dispatchEvents( - (Event[]) resultCache.toArray( - new Event[resultCache.size()])); - resultCache.clear(); - } - } - } finally { - monitor.done(); - } - return errors.getStatus(); - } - /** * Collect the calculated synchronization information for the given resource at the given depth. The * results are added to the provided list. */ @@ -302,13 +150,14 @@ public class SubscriberEventHandler { // resource is no longer under the subscriber control if (info == null) { results.add( - new Event(resource, Event.REMOVAL, IResource.DEPTH_ZERO)); + new SubscriberEvent(resource, SubscriberEvent.REMOVAL, IResource.DEPTH_ZERO)); } else { results.add( - new Event(resource, Event.CHANGE, IResource.DEPTH_ZERO, info)); + new SubscriberEvent(resource, SubscriberEvent.CHANGE, IResource.DEPTH_ZERO, info)); } monitor.worked(1); } + /** * Called to initialize to calculate the synchronization information using the optimized subscriber method. For * subscribers that don't support the optimization, all resources in the subscriber are manually re-calculated. @@ -318,7 +167,7 @@ public class SubscriberEventHandler { * @return Event[] the change events * @throws TeamException */ - private Event[] getAllOutOfSync( + private SubscriberEvent[] getAllOutOfSync( IResource[] resources, int depth, IProgressMonitor monitor) @@ -342,14 +191,14 @@ public class SubscriberEventHandler { subMonitor, events); } - return (Event[]) events.toArray(new Event[events.size()]); + return (SubscriberEvent[]) events.toArray(new SubscriberEvent[events.size()]); // The subscriber has returned the list of out-of-sync resources. } else { - Event[] events = new Event[infos.length]; + SubscriberEvent[] events = new SubscriberEvent[infos.length]; for (int i = 0; i < infos.length; i++) { SyncInfo info = infos[i]; events[i] = - new Event(info.getLocal(), Event.CHANGE, depth, info); + new SubscriberEvent(info.getLocal(), SubscriberEvent.CHANGE, depth, info); } return events; } @@ -363,16 +212,16 @@ public class SubscriberEventHandler { * for each event type. * @param events */ - private void dispatchEvents(Event[] events) { + private void dispatchEvents(SubscriberEvent[] events) { // this will batch the following set changes until endInput is called. set.getSyncSet().beginInput(); for (int i = 0; i < events.length; i++) { - Event event = events[i]; + SubscriberEvent event = events[i]; switch (event.getType()) { - case Event.CHANGE : + case SubscriberEvent.CHANGE : set.collect(event.getResult()); break; - case Event.REMOVAL : + case SubscriberEvent.REMOVAL : if (event.getDepth() == IResource.DEPTH_INFINITE) { set.getSyncSet().removeAllChildren(event.getResource()); } else { @@ -383,6 +232,7 @@ public class SubscriberEventHandler { } set.getSyncSet().endInput(); } + /** * Initialize all resources for the subscriber associated with the set. This will basically recalculate * all synchronization information for the subscriber. @@ -392,11 +242,62 @@ public class SubscriberEventHandler { private void reset(int type) { IResource[] resources = set.getSubscriber().roots(); for (int i = 0; i < resources.length; i++) { - queueEvent(new Event(resources[i], type, IResource.DEPTH_INFINITE)); + queueEvent(new SubscriberEvent(resources[i], type, IResource.DEPTH_INFINITE)); } } - /* internal use only */ boolean hasUnprocessedEvents() { - return !awaitingProcessing.isEmpty(); + + /* (non-Javadoc) + * @see org.eclipse.team.core.subscribers.BackgroundEventHandler#getName() + */ + public String getName() { + return Policy.bind("SubscriberEventHandler.jobName"); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.team.core.subscribers.BackgroundEventHandler#getErrorsTitle() + */ + public String getErrorsTitle() { + return Policy.bind("SubscriberEventHandler.errors"); //$NON-NLS-1$; + } + + /* (non-Javadoc) + * @see org.eclipse.team.core.subscribers.BackgroundEventHandler#processEvent(org.eclipse.team.core.subscribers.BackgroundEventHandler.Event, org.eclipse.core.runtime.IProgressMonitor) + */ + protected void processEvent(Event event, IProgressMonitor monitor) throws TeamException { + // Cancellation is dangerous because this will leave the sync info in a bad state. + // Purposely not checking - + int type = event.getType(); + switch (type) { + case SubscriberEvent.REMOVAL : + resultCache.add(event); + break; + case SubscriberEvent.CHANGE : + List results = new ArrayList(); + collect( + event.getResource(), + event.getDepth(), + monitor, + results); + resultCache.addAll(results); + break; + case SubscriberEvent.INITIALIZE : + monitor.subTask(Policy.bind("SubscriberEventHandler.2", event.getResource().getFullPath().toString())); //$NON-NLS-1$ + SubscriberEvent[] events = + getAllOutOfSync( + new IResource[] { event.getResource()}, + event.getDepth(), + Policy.subMonitorFor(monitor, 64)); + resultCache.addAll(Arrays.asList(events)); + break; + } + + if (!hasUnprocessedEvents() + || resultCache.size() > NOTIFICATION_BATCHING_NUMBER) { + dispatchEvents( + (SubscriberEvent[]) resultCache.toArray( + new SubscriberEvent[resultCache.size()])); + resultCache.clear(); + } } } |