1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.dfs;
12
13 import static java.util.stream.Collectors.joining;
14
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import org.eclipse.jgit.internal.storage.pack.PackExt;
30 import org.eclipse.jgit.lib.AnyObjectId;
31 import org.eclipse.jgit.lib.ObjectDatabase;
32 import org.eclipse.jgit.lib.ObjectInserter;
33 import org.eclipse.jgit.lib.ObjectReader;
34
35
36
37
38
39
40 public abstract class DfsObjDatabase extends ObjectDatabase {
41 private static final PackList NO_PACKS = new PackList(
42 new DfsPackFile[0],
43 new DfsReftable[0]) {
44 @Override
45 boolean dirty() {
46 return true;
47 }
48
49 @Override
50 void clearDirty() {
51
52 }
53
54 @Override
55 public void markDirty() {
56
57 }
58 };
59
60
61
62
63
64
65
66
67 public enum PackSource {
68
69 INSERT,
70
71
72
73
74
75
76
77
78
79
80 RECEIVE,
81
82
83
84
85
86
87
88
89
90
91 COMPACT,
92
93
94
95
96
97
98
99
100
101
102 GC,
103
104
105 GC_REST,
106
107
108
109
110
111
112
113
114 UNREACHABLE_GARBAGE;
115
116
117
118
119
120
121
122
123 public static final Comparator<PackSource> DEFAULT_COMPARATOR =
124 new ComparatorBuilder()
125 .add(INSERT, RECEIVE)
126 .add(COMPACT)
127 .add(GC)
128 .add(GC_REST)
129 .add(UNREACHABLE_GARBAGE)
130 .build();
131
132
133
134
135
136 public static class ComparatorBuilder {
137 private final Map<PackSource, Integer> ranks = new HashMap<>();
138 private int counter;
139
140
141
142
143
144
145
146
147
148
149
150 public ComparatorBuilder add(PackSource... sources) {
151 for (PackSource s : sources) {
152 ranks.put(s, Integer.valueOf(counter));
153 }
154 counter++;
155 return this;
156 }
157
158
159
160
161
162
163
164
165
166 public Comparator<PackSource> build() {
167 return new PackSourceComparator(ranks);
168 }
169 }
170
171 private static class PackSourceComparator implements Comparator<PackSource> {
172 private final Map<PackSource, Integer> ranks;
173
174 private PackSourceComparator(Map<PackSource, Integer> ranks) {
175 if (!ranks.keySet().equals(
176 new HashSet<>(Arrays.asList(PackSource.values())))) {
177 throw new IllegalArgumentException();
178 }
179 this.ranks = new HashMap<>(ranks);
180 }
181
182 @Override
183 public int compare(PackSource a, PackSource b) {
184 return ranks.get(a).compareTo(ranks.get(b));
185 }
186
187 @Override
188 public String toString() {
189 return Arrays.stream(PackSource.values())
190 .map(s -> s + "=" + ranks.get(s))
191 .collect(joining(", ", getClass().getSimpleName() + "{", "}"));
192 }
193 }
194 }
195
196 private final AtomicReference<PackList> packList;
197
198 private final DfsRepository repository;
199
200 private DfsReaderOptions readerOptions;
201
202 private Comparator<DfsPackDescription> packComparator;
203
204
205
206
207
208
209
210
211
212 protected DfsObjDatabase(DfsRepository repository,
213 DfsReaderOptions options) {
214 this.repository = repository;
215 this.packList = new AtomicReference<>(NO_PACKS);
216 this.readerOptions = options;
217 this.packComparator = DfsPackDescription.objectLookupComparator();
218 }
219
220
221
222
223
224
225 public DfsReaderOptions getReaderOptions() {
226 return readerOptions;
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240 public void setPackComparator(Comparator<DfsPackDescription> packComparator) {
241 this.packComparator = packComparator;
242 }
243
244
245 @Override
246 public DfsReader newReader() {
247 return new DfsReader(this);
248 }
249
250
251 @Override
252 public ObjectInserter newInserter() {
253 return new DfsInserter(this);
254 }
255
256
257
258
259
260
261
262
263
264 public DfsPackFile[] getPacks() throws IOException {
265 return getPackList().packs;
266 }
267
268
269
270
271
272
273
274
275
276 public DfsReftable[] getReftables() throws IOException {
277 return getPackList().reftables;
278 }
279
280
281
282
283
284
285
286
287
288
289 public PackList getPackList() throws IOException {
290 return scanPacks(NO_PACKS);
291 }
292
293
294
295
296
297
298 protected DfsRepository getRepository() {
299 return repository;
300 }
301
302
303
304
305
306
307
308 public DfsPackFile[] getCurrentPacks() {
309 return getCurrentPackList().packs;
310 }
311
312
313
314
315
316
317
318 public DfsReftable[] getCurrentReftables() {
319 return getCurrentPackList().reftables;
320 }
321
322
323
324
325
326
327
328
329 public PackList getCurrentPackList() {
330 return packList.get();
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347 public boolean has(AnyObjectId objectId, boolean avoidUnreachableObjects)
348 throws IOException {
349 try (ObjectReader or = newReader()) {
350 or.setAvoidUnreachableObjects(avoidUnreachableObjects);
351 return or.has(objectId);
352 }
353 }
354
355
356
357
358
359
360
361
362
363
364
365 protected abstract DfsPackDescription newPack(PackSource source)
366 throws IOException;
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 protected DfsPackDescription newPack(PackSource source,
389 long estimatedPackSize) throws IOException {
390 DfsPackDescription pack = newPack(source);
391 pack.setEstimatedPackSize(estimatedPackSize);
392 return pack;
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420 protected void commitPack(Collection<DfsPackDescription> desc,
421 Collection<DfsPackDescription> replaces) throws IOException {
422 commitPackImpl(desc, replaces);
423 getRepository().fireEvent(new DfsPacksChangedEvent());
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437 protected abstract void commitPackImpl(Collection<DfsPackDescription> desc,
438 Collection<DfsPackDescription> replaces) throws IOException;
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455 protected abstract void rollbackPack(Collection<DfsPackDescription> desc);
456
457
458
459
460
461
462
463
464
465
466
467
468
469 protected abstract List<DfsPackDescription> listPacks() throws IOException;
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 protected abstract ReadableChannel openFile(
488 DfsPackDescription desc, PackExt ext)
489 throws FileNotFoundException, IOException;
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504 protected abstract DfsOutputStream writeFile(
505 DfsPackDescription desc, PackExt ext) throws IOException;
506
507 void addPack(DfsPackFile newPack) throws IOException {
508 PackList o, n;
509 do {
510 o = packList.get();
511 if (o == NO_PACKS) {
512
513
514
515
516
517 o = scanPacks(o);
518
519
520
521
522 for (DfsPackFile p : o.packs) {
523 if (p.key.equals(newPack.key)) {
524 return;
525 }
526 }
527 }
528
529 DfsPackFile[] packs = new DfsPackFile[1 + o.packs.length];
530 packs[0] = newPack;
531 System.arraycopy(o.packs, 0, packs, 1, o.packs.length);
532 n = new PackListImpl(packs, o.reftables);
533 } while (!packList.compareAndSet(o, n));
534 }
535
536 void addReftable(DfsPackDescription add, Set<DfsPackDescription> remove)
537 throws IOException {
538 PackList o, n;
539 do {
540 o = packList.get();
541 if (o == NO_PACKS) {
542 o = scanPacks(o);
543 for (DfsReftable t : o.reftables) {
544 if (t.getPackDescription().equals(add)) {
545 return;
546 }
547 }
548 }
549
550 List<DfsReftable> tables = new ArrayList<>(1 + o.reftables.length);
551 for (DfsReftable t : o.reftables) {
552 if (!remove.contains(t.getPackDescription())) {
553 tables.add(t);
554 }
555 }
556 tables.add(new DfsReftable(add));
557 n = new PackListImpl(o.packs, tables.toArray(new DfsReftable[0]));
558 } while (!packList.compareAndSet(o, n));
559 }
560
561 PackList scanPacks(PackList original) throws IOException {
562 PackList o, n;
563 synchronized (packList) {
564 do {
565 o = packList.get();
566 if (o != original) {
567
568
569
570 return o;
571 }
572 n = scanPacksImpl(o);
573 if (n == o)
574 return n;
575 } while (!packList.compareAndSet(o, n));
576 }
577 getRepository().fireEvent(new DfsPacksChangedEvent());
578 return n;
579 }
580
581 private PackList scanPacksImpl(PackList old) throws IOException {
582 DfsBlockCache cache = DfsBlockCache.getInstance();
583 Map<DfsPackDescription, DfsPackFile> packs = packMap(old);
584 Map<DfsPackDescription, DfsReftable> reftables = reftableMap(old);
585
586 List<DfsPackDescription> scanned = listPacks();
587 Collections.sort(scanned, packComparator);
588
589 List<DfsPackFile> newPacks = new ArrayList<>(scanned.size());
590 List<DfsReftable> newReftables = new ArrayList<>(scanned.size());
591 boolean foundNew = false;
592 for (DfsPackDescription dsc : scanned) {
593 DfsPackFile oldPack = packs.remove(dsc);
594 if (oldPack != null) {
595 newPacks.add(oldPack);
596 } else if (dsc.hasFileExt(PackExt.PACK)) {
597 newPacks.add(new DfsPackFile(cache, dsc));
598 foundNew = true;
599 }
600
601 DfsReftable oldReftable = reftables.remove(dsc);
602 if (oldReftable != null) {
603 newReftables.add(oldReftable);
604 } else if (dsc.hasFileExt(PackExt.REFTABLE)) {
605 newReftables.add(new DfsReftable(cache, dsc));
606 foundNew = true;
607 }
608 }
609
610 if (newPacks.isEmpty() && newReftables.isEmpty())
611 return new PackListImpl(NO_PACKS.packs, NO_PACKS.reftables);
612 if (!foundNew) {
613 old.clearDirty();
614 return old;
615 }
616 Collections.sort(newReftables, reftableComparator());
617 return new PackListImpl(
618 newPacks.toArray(new DfsPackFile[0]),
619 newReftables.toArray(new DfsReftable[0]));
620 }
621
622 private static Map<DfsPackDescription, DfsPackFile> packMap(PackList old) {
623 Map<DfsPackDescription, DfsPackFile> forReuse = new HashMap<>();
624 for (DfsPackFile p : old.packs) {
625 if (!p.invalid()) {
626 forReuse.put(p.desc, p);
627 }
628 }
629 return forReuse;
630 }
631
632 private static Map<DfsPackDescription, DfsReftable> reftableMap(PackList old) {
633 Map<DfsPackDescription, DfsReftable> forReuse = new HashMap<>();
634 for (DfsReftable p : old.reftables) {
635 if (!p.invalid()) {
636 forReuse.put(p.desc, p);
637 }
638 }
639 return forReuse;
640 }
641
642
643
644
645
646
647 protected Comparator<DfsReftable> reftableComparator() {
648 return Comparator.comparing(
649 DfsReftable::getPackDescription,
650 DfsPackDescription.reftableComparator());
651 }
652
653
654
655
656 protected void clearCache() {
657 packList.set(NO_PACKS);
658 }
659
660
661 @Override
662 public void close() {
663 packList.set(NO_PACKS);
664 }
665
666
667 public abstract static class PackList {
668
669 public final DfsPackFile[] packs;
670
671
672 public final DfsReftable[] reftables;
673
674 private long lastModified = -1;
675
676 PackList(DfsPackFile[] packs, DfsReftable[] reftables) {
677 this.packs = packs;
678 this.reftables = reftables;
679 }
680
681
682 public long getLastModified() {
683 if (lastModified < 0) {
684 long max = 0;
685 for (DfsPackFile pack : packs) {
686 max = Math.max(max, pack.getPackDescription().getLastModified());
687 }
688 lastModified = max;
689 }
690 return lastModified;
691 }
692
693 abstract boolean dirty();
694 abstract void clearDirty();
695
696
697
698
699
700
701
702
703 public abstract void markDirty();
704 }
705
706 private static final class PackListImpl extends PackList {
707 private volatile boolean dirty;
708
709 PackListImpl(DfsPackFile[] packs, DfsReftable[] reftables) {
710 super(packs, reftables);
711 }
712
713 @Override
714 boolean dirty() {
715 return dirty;
716 }
717
718 @Override
719 void clearDirty() {
720 dirty = false;
721 }
722
723 @Override
724 public void markDirty() {
725 dirty = true;
726 }
727 }
728 }