Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 947d027e5e3d5ca1388e7bc7cb81b12dccbdf8ce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*******************************************************************************
 * Copyright (c) 2009, 2012 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.common.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.common.core.utility.command.JobCommand;
import org.eclipse.jpt.common.utility.internal.ListenerList;
import org.eclipse.jpt.common.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;
 * preventing the synchronization from quiescing.
 */
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.common.utility.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();
		}
	}
}

Back to the top