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