Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 67f67b5265cec7f5b33b1bde74cf93b578debcf7 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
/*
 * Copyright (c) OSGi Alliance (2010, 2013). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.osgi.service.coordinator;

import java.util.List;
import java.util.Map;
import org.osgi.framework.Bundle;

/**
 * A Coordination object is used to coordinate a number of independent
 * Participants.
 * 
 * <p>
 * Once a Coordination is {@link Coordinator#create(String, long) created}, it
 * can be used to add {@link Participant} objects. When the Coordination is
 * ended, the participants are {@link Participant#ended(Coordination) notified}.
 * A Coordination can also fail for various reasons. When this occurs, the
 * participants are {@link Participant#failed(Coordination) notified} of the
 * failure.
 * 
 * <p>
 * A Coordination must be in one of two states, either ACTIVE or TERMINATED. The
 * transition between ACTIVE and TERMINATED must be atomic, ensuring that a
 * Participant can be guaranteed of either receiving an exception when adding
 * itself to a Coordination or of receiving notification the Coordination has
 * terminated.
 * 
 * <p>
 * A Coordination object is thread safe and can be passed as a parameter to
 * other parties regardless of the threads these parties use.
 * 
 * <p>
 * The following example code shows how a Coordination should be used.
 * 
 * <pre>
 * void foo() {
 *   Coordination c = coordinator.create(&quot;work&quot;, 0);
 *   try {
 *     doWork(c);
 *   }
 *   catch (Exception e) {
 *     c.fail(e);
 *   }
 *   finally {
 *     c.end();
 *   }
 * }
 * </pre>
 * 
 * @ThreadSafe
 * @noimplement
 * @author $Id$
 */

public interface Coordination {

	/**
	 * A singleton exception that will be the failure cause when a Coordination
	 * times out.
	 */
	Exception	TIMEOUT		= new Exception("TIMEOUT");

	/**
	 * A singleton exception that will be the failure cause when the
	 * Coordinations created by a bundle are terminated because the bundle
	 * released the Coordinator service.
	 */
	Exception	RELEASED	= new Exception("RELEASED");

	/**
	 * A singleton exception that will be the failure cause when a Coordination
	 * has been orphaned.
	 */
	Exception	ORPHANED	= new Exception("ORPHANED");

	/**
	 * Returns the id assigned to this Coordination.
	 * 
	 * The id is assigned by the {@link Coordinator} service which created this
	 * Coordination and is unique among all the Coordinations created by the
	 * Coordinator service and must not be reused as long as the Coordinator
	 * service remains registered. The id must be positive and monotonically
	 * increases for each Coordination created by the Coordinator service.
	 * 
	 * @return The id assigned to this Coordination.
	 */
	long getId();

	/**
	 * Returns the name of this Coordination.
	 * 
	 * The name is specified when this Coordination was created.
	 * 
	 * @return The name of this Coordination.
	 * 
	 */
	String getName();

	/**
	 * Terminate this Coordination normally.
	 * 
	 * <p>
	 * If this Coordination has been {@link #push() pushed} on the thread local
	 * Coordination stack of another thread, this method does nothing except
	 * throw a CoordinationException of type
	 * {@link CoordinationException#WRONG_THREAD}.
	 * 
	 * <p>
	 * If this Coordination has been {@link #push() pushed} on the thread local
	 * Coordination stack of this thread but is not the
	 * {@link Coordinator#peek() current Coordination}, then the Coordinations
	 * on the thread local Coordination stack above this Coordination must be
	 * terminated and removed from the thread local Coordination stack before
	 * this Coordination is terminated. Each of these Coordinations, starting
	 * with the current Coordination, will be {@link #end() terminated normally}
	 * . If the termination throws a {@link CoordinationException}, then the
	 * next Coordination on the thread local Coordination stack will be
	 * {@link #fail(Throwable) terminated as a failure} with a failure cause of
	 * the thrown CoordinationException. At the end of this process, this
	 * Coordination will be the current Coordination and will have been
	 * terminated as a failure if any of the terminated Coordinations threw a
	 * CoordinationException
	 * 
	 * <p>
	 * If this Coordination is the {@link Coordinator#peek() current
	 * Coordination}, then it will be {@link Coordinator#pop() removed} from the
	 * thread local Coordination stack.
	 * 
	 * <p>
	 * If this Coordination is already terminated, a CoordinationException is
	 * thrown. If this Coordination was terminated as a failure, the
	 * {@link #getFailure() failure cause} will be the cause of the thrown
	 * CoordinationException.
	 * 
	 * <p>
	 * Otherwise, this Coordination is terminated normally and then all
	 * registered {@link #getParticipants() Participants} are
	 * {@link Participant#ended(Coordination) notified}. Participants should
	 * finalize any work associated with this Coordination. The successful
	 * return of this method indicates that the Coordination has terminated
	 * normally and all registered Participants have been notified of the normal
	 * termination.
	 * 
	 * <p>
	 * It is possible that one of the Participants throws an exception during
	 * notification. If this happens, this Coordination is considered to have
	 * partially failed and this method must throw a CoordinationException of
	 * type {@link CoordinationException#PARTIALLY_ENDED} after all the
	 * registered Participants have been notified.
	 * 
	 * @throws CoordinationException If this Coordination has failed, including
	 *         timed out, or partially failed or this Coordination is on the
	 *         thread local Coordination stack of another thread.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[INITIATE]} for this Coordination.
	 */
	void end();

	/**
	 * Terminate this Coordination as a failure with the specified failure
	 * cause.
	 * 
	 * <p>
	 * If this Coordination is already {@link #isTerminated() terminated}, this
	 * method does nothing and returns {@code false}.
	 * 
	 * <p>
	 * Otherwise, this Coordination is terminated as a failure with the
	 * specified failure cause and then all registered
	 * {@link #getParticipants() Participants} are
	 * {@link Participant#failed(Coordination) notified}. Participants should
	 * discard any work associated with this Coordination. This method will
	 * return {@code true}.
	 * 
	 * <p>
	 * If this Coordination has been {@link #push() pushed} onto a thread local
	 * Coordination stack, this Coordination is not removed from the stack. The
	 * creator of this Coordination must still call {@link #end()} on this
	 * Coordination to cause it to be removed from the thread local Coordination
	 * stack.
	 * 
	 * @param cause The failure cause. The failure cause must not be
	 *        {@code null}.
	 * @return {@code true} if this Coordination was active and was terminated
	 *         by this method, otherwise {@code false}.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[PARTICIPATE]} for this
	 *         Coordination.
	 */
	boolean fail(Throwable cause);

	/**
	 * Returns the failure cause of this Coordination.
	 * 
	 * <p>
	 * If this Coordination has {@link #fail(Throwable) failed}, then this
	 * method will return the failure cause.
	 * 
	 * <p>
	 * If this Coordination timed out, this method will return {@link #TIMEOUT}
	 * as the failure cause. If this Coordination was active when the bundle
	 * that created it released the {@link Coordinator} service, this method
	 * will return {@link #RELEASED} as the failure cause. If the Coordination
	 * was orphaned, this method will return {@link #ORPHANED} as the failure
	 * cause.
	 * 
	 * @return The failure cause of this Coordination or {@code null} if this
	 *         Coordination has not terminated as a failure.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[INITIATE]} for this Coordination.
	 */
	Throwable getFailure();

	/**
	 * Returns whether this Coordination is terminated.
	 * 
	 * @return {@code true} if this Coordination is terminated, otherwise
	 *         {@code false} if this Coordination is active.
	 */
	boolean isTerminated();

	/**
	 * Register a Participant with this Coordination.
	 * 
	 * <p>
	 * Once a Participant is registered with this Coordination, it is guaranteed
	 * to receive a notification for either
	 * {@link Participant#ended(Coordination) normal} or
	 * {@link Participant#failed(Coordination) failure} termination when this
	 * Coordination is terminated.
	 * 
	 * <p>
	 * Participants are registered using their object identity. Once a
	 * Participant is registered with this Coordination, subsequent attempts to
	 * register the Participant again with this Coordination are ignored and the
	 * Participant is only notified once when this Coordination is terminated.
	 * 
	 * <p>
	 * A Participant can only be registered with a single active Coordination at
	 * a time. If a Participant is already registered with an active
	 * Coordination, attempts to register the Participation with another active
	 * Coordination will block until the Coordination the Participant is
	 * registered with terminates. Notice that in edge cases the notification to
	 * the Participant that this Coordination has terminated can happen before
	 * this method returns.
	 * 
	 * <p>
	 * Attempting to register a Participant with a {@link #isTerminated()
	 * terminated} Coordination will result in a CoordinationException being
	 * thrown.
	 * 
	 * <p>
	 * The ordering of notifying Participants must follow the reverse order in
	 * which the Participants were registered.
	 * 
	 * @param participant The Participant to register with this Coordination.
	 *        The participant must not be {@code null}.
	 * @throws CoordinationException If the Participant could not be registered
	 *         with this Coordination. This exception should normally not be
	 *         caught by the caller but allowed to be caught by the initiator of
	 *         this Coordination.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[PARTICIPATE]} for this
	 *         Coordination.
	 */
	void addParticipant(Participant participant);

	/**
	 * Returns a snapshot of the Participants registered with this Coordination.
	 * 
	 * @return A snapshot of the Participants registered with this Coordination.
	 *         If no Participants are registered with this Coordination, the
	 *         returned list will be empty. The list is ordered in the order the
	 *         Participants were registered. The returned list is the property
	 *         of the caller and can be modified by the caller.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[INITIATE]} for this Coordination.
	 */
	List<Participant> getParticipants();

	/**
	 * Returns the variable map associated with this Coordination.
	 * 
	 * Each Coordination has a map that can be used for communicating between
	 * different Participants. The key of the map is a class, allowing for
	 * private data to be stored in the map by using implementation classes or
	 * shared data by using shared interfaces.
	 * 
	 * The returned map is not synchronized. Users of the map must synchronize
	 * on the Map object while making changes.
	 * 
	 * @return The variable map associated with this Coordination.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[PARTICIPANT]} for this
	 *         Coordination.
	 */
	Map<Class<?>, Object> getVariables();

	/**
	 * Extend the time out of this Coordination.
	 * 
	 * <p>
	 * Participants can call this method to extend the timeout of this
	 * Coordination with at least the specified time. This can be done by
	 * Participants when they know a task will take more than normal time.
	 * 
	 * <p>
	 * This method will return the new deadline if an extension took place or
	 * the current deadline if, for whatever reason, no extension takes place.
	 * Note that if a maximum timeout is in effect, the deadline may not be
	 * extended by as much as was requested, if at all. If there is no deadline,
	 * zero is returned. Specifying a timeout extension of 0 will return the
	 * existing deadline.
	 * 
	 * @param timeMillis The time in milliseconds to extend the current timeout.
	 *        If the initial timeout was specified as 0, no extension must take
	 *        place. A zero must have no effect.
	 * @return The new deadline in milliseconds. If the specified time is 0, the
	 *         existing deadline is returned. If this Coordination was created
	 *         with an initial timeout of 0, no timeout is set and 0 is
	 *         returned.
	 * @throws CoordinationException If this Coordination
	 *         {@link #isTerminated() is terminated}.
	 * @throws IllegalArgumentException If the specified time is negative.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[PARTICIPATE]} for this
	 *         Coordination.
	 */
	long extendTimeout(long timeMillis);

	/**
	 * Wait until this Coordination is terminated and all registered
	 * Participants have been notified.
	 * 
	 * @param timeMillis Maximum time in milliseconds to wait. Specifying a time
	 *        of 0 will wait until this Coordination is terminated.
	 * @throws InterruptedException If the wait is interrupted.
	 * @throws IllegalArgumentException If the specified time is negative.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[PARTICIPATE]} for this
	 *         Coordination.
	 */

	void join(long timeMillis) throws InterruptedException;

	/**
	 * Push this Coordination object onto the thread local Coordination stack to
	 * make it the {@link Coordinator#peek() current Coordination}.
	 * 
	 * @return This Coordination.
	 * @throws CoordinationException If this Coordination is already on the any
	 *         thread's thread local Coordination stack or this Coordination
	 *         {@link #isTerminated() is terminated}.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[INITIATE]} for this Coordination.
	 */
	Coordination push();

	/**
	 * Returns the thread in whose thread local Coordination stack this
	 * Coordination has been {@link #push() pushed}.
	 * 
	 * @return The thread in whose thread local Coordination stack this
	 *         Coordination has been pushed or {@code null} if this Coordination
	 *         is not in any thread local Coordination stack.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[ADMIN]} for this Coordination.
	 */
	Thread getThread();

	/**
	 * Returns the bundle that created this Coordination. This is the bundle
	 * that obtained the {@link Coordinator} service that was used to create
	 * this Coordination.
	 * 
	 * @return The bundle that created this Coordination.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[ADMIN]} for this Coordination.
	 */
	Bundle getBundle();

	/**
	 * Returns the Coordination enclosing this Coordination if this Coordination
	 * is on the thread local Coordination stack.
	 * 
	 * <p>
	 * When a Coordination is {@link #push() pushed} onto the thread local
	 * Coordination stack, the former {@link Coordinator#peek() current
	 * Coordination}, if any, is the enclosing Coordination of this
	 * Coordination. When this Coordination is {@link Coordinator#pop() removed}
	 * from the thread local Coordination stack, this Coordination no longer has
	 * an enclosing Coordination.
	 * 
	 * @return The Coordination enclosing this Coordination if this Coordination
	 *         is on the thread local Coordination stack or {@code null} if this
	 *         Coordination is not on the thread local Coordination stack or has
	 *         no enclosing Coordination.
	 * @throws SecurityException If the caller does not have
	 *         {@code CoordinationPermission[ADMIN]} for this Coordination.
	 */
	Coordination getEnclosingCoordination();
}

Back to the top