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