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 package org.eclipse.jgit.internal.ketch;
45
46 import static org.eclipse.jgit.internal.ketch.Proposal.State.ABORTED;
47 import static org.eclipse.jgit.internal.ketch.Proposal.State.EXECUTED;
48 import static org.eclipse.jgit.internal.ketch.Proposal.State.NEW;
49 import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
50 import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
51
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.List;
57 import java.util.concurrent.CopyOnWriteArrayList;
58 import java.util.concurrent.TimeUnit;
59 import java.util.concurrent.atomic.AtomicReference;
60
61 import org.eclipse.jgit.annotations.Nullable;
62 import org.eclipse.jgit.errors.MissingObjectException;
63 import org.eclipse.jgit.internal.storage.reftree.Command;
64 import org.eclipse.jgit.lib.ObjectId;
65 import org.eclipse.jgit.lib.PersonIdent;
66 import org.eclipse.jgit.lib.Ref;
67 import org.eclipse.jgit.revwalk.RevWalk;
68 import org.eclipse.jgit.transport.PushCertificate;
69 import org.eclipse.jgit.transport.ReceiveCommand;
70 import org.eclipse.jgit.util.time.ProposedTimestamp;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public class Proposal {
88
89 public enum State {
90
91 NEW(false),
92
93
94
95
96
97 QUEUED(false),
98
99
100 RUNNING(false),
101
102
103
104
105
106
107 EXECUTED(true),
108
109
110 ABORTED(true);
111
112 private final boolean done;
113
114 private State(boolean done) {
115 this.done = done;
116 }
117
118
119 public boolean isDone() {
120 return done;
121 }
122 }
123
124 private final List<Command> commands;
125 private PersonIdent author;
126 private String message;
127 private PushCertificate pushCert;
128
129 private List<ProposedTimestamp> timestamps;
130 private final List<Runnable> listeners = new CopyOnWriteArrayList<>();
131 private final AtomicReference<State> state = new AtomicReference<>(NEW);
132
133
134
135
136
137
138
139 public Proposal(List<Command> cmds) {
140 commands = Collections.unmodifiableList(new ArrayList<>(cmds));
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155 public Proposal(RevWalk rw, Collection<ReceiveCommand> cmds)
156 throws MissingObjectException, IOException {
157 commands = asCommandList(rw, cmds);
158 }
159
160 private static List<Command> asCommandList(RevWalk rw,
161 Collection<ReceiveCommand> cmds)
162 throws MissingObjectException, IOException {
163 List<Command> commands = new ArrayList<>(cmds.size());
164 for (ReceiveCommand cmd : cmds) {
165 commands.add(new Command(rw, cmd));
166 }
167 return Collections.unmodifiableList(commands);
168 }
169
170
171
172
173
174
175 public Collection<Command> getCommands() {
176 return commands;
177 }
178
179
180
181
182
183
184 @Nullable
185 public PersonIdent getAuthor() {
186 return author;
187 }
188
189
190
191
192
193
194
195
196 public Proposal setAuthor(@Nullable PersonIdent who) {
197 author = who;
198 return this;
199 }
200
201
202
203
204
205
206 @Nullable
207 public String getMessage() {
208 return message;
209 }
210
211
212
213
214
215
216
217
218 public Proposal setMessage(@Nullable String msg) {
219 message = msg != null && !msg.isEmpty() ? msg : null;
220 return this;
221 }
222
223
224
225
226
227
228 @Nullable
229 public PushCertificate getPushCertificate() {
230 return pushCert;
231 }
232
233
234
235
236
237
238
239
240 public Proposal setPushCertificate(@Nullable PushCertificate cert) {
241 pushCert = cert;
242 return this;
243 }
244
245
246
247
248
249
250
251 public List<ProposedTimestamp> getProposedTimestamps() {
252 if (timestamps != null) {
253 return timestamps;
254 }
255 return Collections.emptyList();
256 }
257
258
259
260
261
262
263
264
265 public Proposal addProposedTimestamp(ProposedTimestamp ts) {
266 if (timestamps == null) {
267 timestamps = new ArrayList<>(4);
268 }
269 timestamps.add(ts);
270 return this;
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 public void addListener(Runnable callback) {
287 boolean runNow = false;
288 synchronized (state) {
289 if (state.get().isDone()) {
290 runNow = true;
291 } else {
292 listeners.add(callback);
293 }
294 }
295 if (runNow) {
296 callback.run();
297 }
298 }
299
300
301 void success() {
302 for (Command c : commands) {
303 if (c.getResult() == NOT_ATTEMPTED) {
304 c.setResult(OK);
305 }
306 }
307 notifyState(EXECUTED);
308 }
309
310
311 void abort() {
312 Command.abort(commands, null);
313 notifyState(ABORTED);
314 }
315
316
317
318
319
320
321 public State getState() {
322 return state.get();
323 }
324
325
326
327
328
329
330
331
332
333 public boolean isDone() {
334 return state.get().isDone();
335 }
336
337
338
339
340
341
342
343 public void await() throws InterruptedException {
344 synchronized (state) {
345 while (!state.get().isDone()) {
346 state.wait();
347 }
348 }
349 }
350
351
352
353
354
355
356
357
358
359
360
361
362 public boolean await(long wait, TimeUnit unit) throws InterruptedException {
363 synchronized (state) {
364 if (state.get().isDone()) {
365 return true;
366 }
367 state.wait(unit.toMillis(wait));
368 return state.get().isDone();
369 }
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385 public boolean awaitStateChange(State notIn, long wait, TimeUnit unit)
386 throws InterruptedException {
387 synchronized (state) {
388 if (state.get() != notIn) {
389 return true;
390 }
391 state.wait(unit.toMillis(wait));
392 return state.get() != notIn;
393 }
394 }
395
396 void notifyState(State s) {
397 synchronized (state) {
398 state.set(s);
399 state.notifyAll();
400 }
401 if (s.isDone()) {
402 for (Runnable callback : listeners) {
403 callback.run();
404 }
405 listeners.clear();
406 }
407 }
408
409
410 @Override
411 public String toString() {
412 StringBuilder s = new StringBuilder();
413 s.append("Ketch Proposal {\n");
414 s.append(" ").append(state.get()).append('\n');
415 if (author != null) {
416 s.append(" author ").append(author).append('\n');
417 }
418 if (message != null) {
419 s.append(" message ").append(message).append('\n');
420 }
421 for (Command c : commands) {
422 s.append(" ");
423 format(s, c.getOldRef(), "CREATE");
424 s.append(' ');
425 format(s, c.getNewRef(), "DELETE");
426 s.append(' ').append(c.getRefName());
427 if (c.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
428 s.append(' ').append(c.getResult());
429 }
430 s.append('\n');
431 }
432 s.append('}');
433 return s.toString();
434 }
435
436 private static void format(StringBuilder s, @Nullable Ref r, String n) {
437 if (r == null) {
438 s.append(n);
439 } else if (r.isSymbolic()) {
440 s.append(r.getTarget().getName());
441 } else {
442 ObjectId id = r.getObjectId();
443 if (id != null) {
444 s.append(id.abbreviate(8).name());
445 }
446 }
447 }
448 }