blob: 0fc44eb08263bde43f59b7a7aad740788813c870 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.core.internal.utility;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jpt.utility.internal.ListenerList;
import org.eclipse.jpt.utility.synchronizers.CallbackSynchronizer;
/**
* Extend the job synchronizer to notify listeners
* when a synchronization "cycle" is complete; i.e. the synchronization has,
* for the moment, handled every "synchronize" request and quiesced.
* This notification is <em>not</em> guaranteed to occur with <em>every</em>
* synchronization "cycle";
* since other, unrelated, synchronizations can be triggered concurrently.
*/
public class CallbackJobSynchronizer
extends JobSynchronizer
implements CallbackSynchronizer
{
private final ListenerList<Listener> listenerList = new ListenerList<Listener>(Listener.class);
// ********** construction **********
/**
* Construct a callback job synchronizer that uses the specified job command to
* perform the synchronization. Assign the generated Eclipse job the
* specified name.
*/
public CallbackJobSynchronizer(String jobName, JobCommand command) {
super(jobName, command);
}
/**
* Construct a callback job synchronizer that uses the specified job command to
* perform the synchronization. Assign the generated Eclipse job the
* specified name and scheduling rule.
*/
public CallbackJobSynchronizer(String jobName, JobCommand command, ISchedulingRule schedulingRule) {
super(jobName, command, schedulingRule);
}
/**
* Build a job that will let us know when the synchronization has
* quiesced.
*/
@Override
SynchronizationJob buildJob(String jobName, JobCommand command, ISchedulingRule schedulingRule) {
return new CallbackSynchronizationJob(jobName, command, schedulingRule);
}
// ********** CallbackSynchronizer implementation **********
public void addListener(Listener listener) {
this.listenerList.add(listener);
}
public void removeListener(Listener listener) {
this.listenerList.remove(listener);
}
/**
* Notify our listeners.
*/
void synchronizationQuiesced() {
for (Listener listener : this.listenerList.getListeners()) {
listener.synchronizationQuiesced(this);
}
}
// ********** synchronization job **********
/**
* Extend {@link JobSynchronizer.SynchronizationJob}
* to notify the synchronizer when the synchronization has quiesced
* (i.e. the command has finished executing and there are no further
* requests for synchronization).
* Because synchronization occurs during the job's execution,
* no other thread will be able to
* initiate another synchronization until the synchronizer's listeners have been
* notified. Note also, the synchronizer's listeners can, themselves,
* trigger another synchronization (by directly or indirectly calling
* {@link org.eclipse.jpt.utility.internal.synchronizers.Synchronizer#synchronize());
* but this synchronization will not occur until <em>after</em> all the
* listeners have been notified.
*/
class CallbackSynchronizationJob
extends SynchronizationJob
{
/**
* When this flag is set to true, the job has been scheduled to run.
* We need this because {@link org.eclipse.core.runtime.jobs.Job Job}
* has no public API for discovering whether a job is "scheduled".
*/
// use 'volatile' because synchronization isn't really required
private volatile boolean scheduled;
CallbackSynchronizationJob(String jobName, JobCommand command, ISchedulingRule schedulingRule) {
super(jobName, command, schedulingRule);
this.scheduled = false;
}
/**
* If we are allowing the job to be scheduled (i.e. {@link #start()}
* was called), set the "scheduled" flag.
*/
@Override
void synchronize() {
if (this.shouldSchedule) {
this.scheduled = true;
}
super.synchronize();
}
/**
* Clear the "scheduled" flag; perform the synchronization; and,
* if the "scheduled" flag is still clear (i.e. there have been no
* further calls to {@link #synchronize()}), notify our listeners.
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
this.scheduled = false;
IStatus status = super.run(monitor);
// hmmm - we will notify listeners even when we our job is "stopped";
// that seems ok... ~bjv
if ( ! this.scheduled) {
CallbackJobSynchronizer.this.synchronizationQuiesced();
}
return status;
}
@Override
void stop() {
this.scheduled = false;
super.stop();
}
}
}