Skip to content

Commit af417ec

Browse files
dukhlovjglickDmytro Ukhlov
authored
[JENKINS-75986] Refactor BuildReferenceMapAdapter (#10946)
Co-authored-by: Jesse Glick <jglick@cloudbees.com> Co-authored-by: Dmytro Ukhlov <dukhlov@csod.com>
1 parent 22a8451 commit af417ec

File tree

10 files changed

+257
-537
lines changed

10 files changed

+257
-537
lines changed

core/src/main/java/hudson/model/RunMap.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public RunMap(@NonNull Job<?, ?> job) {
9595
* Used to create new instance of {@link Run}.
9696
* @since 2.451
9797
*/
98-
public RunMap(@NonNull Job<?, ?> job, Constructor cons) {
98+
public RunMap(@NonNull Job<?, ?> job, Constructor<R> cons) {
9999
this.job = Objects.requireNonNull(job);
100100
this.cons = cons;
101101
initBaseDir(job.getBuildDir());
@@ -105,7 +105,7 @@ public RunMap(@NonNull Job<?, ?> job, Constructor cons) {
105105
* @deprecated Use {@link #RunMap(Job, Constructor)}.
106106
*/
107107
@Deprecated
108-
public RunMap(File baseDir, Constructor cons) {
108+
public RunMap(File baseDir, Constructor<R> cons) {
109109
job = null;
110110
this.cons = cons;
111111
initBaseDir(baseDir);
@@ -187,6 +187,8 @@ public R oldestValue() {
187187
*/
188188
public interface Constructor<R extends Run<?, R>> {
189189
R create(File dir) throws IOException;
190+
191+
Class<R> getBuildClass();
190192
}
191193

192194
@Override
@@ -201,7 +203,7 @@ protected String getIdOf(R r) {
201203

202204
/**
203205
* Add a <em>new</em> build to the map.
204-
* Do not use when loading existing builds (use {@link #put(Integer, Object)}).
206+
* Do not use when loading existing builds (use {@link #putAll(Map)}).
205207
*/
206208
@Override
207209
public R put(R r) {
@@ -298,6 +300,11 @@ protected R retrieve(File d) throws IOException {
298300
return null;
299301
}
300302

303+
@Override
304+
protected Class<R> getBuildClass() {
305+
return cons.getBuildClass();
306+
}
307+
301308
/**
302309
* Backward compatibility method that notifies {@link RunMap} of who the owner is.
303310
*

core/src/main/java/hudson/util/Iterators.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.NoSuchElementException;
3636
import java.util.Objects;
3737
import java.util.Set;
38+
import java.util.function.Function;
3839
import org.kohsuke.accmod.Restricted;
3940
import org.kohsuke.accmod.restrictions.NoExternalUse;
4041

@@ -326,6 +327,18 @@ public static <T> Iterator<T> removeNull(final Iterator<T> itr) {
326327
return com.google.common.collect.Iterators.filter(itr, Objects::nonNull);
327328
}
328329

330+
/**
331+
* Wraps another iterator and map iterable objects.
332+
*/
333+
public static <T, U> Iterator<U> map(final Iterator<T> itr, Function<T, U> mapper) {
334+
return new AdaptedIterator<>(itr) {
335+
@Override
336+
protected U adapt(T item) {
337+
return mapper.apply(item);
338+
}
339+
};
340+
}
341+
329342
/**
330343
* Returns an {@link Iterable} that iterates over all the given {@link Iterable}s.
331344
*

core/src/main/java/jenkins/model/lazy/AbstractLazyLoadRunMap.java

Lines changed: 34 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@
3535
import hudson.util.CopyOnWriteMap;
3636
import java.io.File;
3737
import java.io.IOException;
38-
import java.util.AbstractCollection;
3938
import java.util.AbstractMap;
40-
import java.util.AbstractSet;
4139
import java.util.Collection;
4240
import java.util.Collections;
4341
import java.util.Comparator;
@@ -46,10 +44,7 @@
4644
import java.util.NoSuchElementException;
4745
import java.util.Set;
4846
import java.util.SortedMap;
49-
import java.util.Spliterator;
50-
import java.util.Spliterators;
5147
import java.util.TreeMap;
52-
import java.util.function.IntConsumer;
5348
import java.util.function.IntPredicate;
5449
import java.util.logging.Level;
5550
import java.util.logging.Logger;
@@ -101,146 +96,28 @@
10196
public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer, R> implements SortedMap<Integer, R> {
10297
private final CopyOnWriteMap.Tree<Integer, BuildReference<R>> core = new CopyOnWriteMap.Tree<>(
10398
Collections.reverseOrder());
104-
105-
private LazyLoadRunMapEntrySet<R> entrySet = new LazyLoadRunMapEntrySet<>(this);
106-
107-
private transient volatile Set<Integer> keySet;
108-
private transient volatile Collection<R> values;
99+
private final BuildReferenceMapAdapter<R> adapter = new BuildReferenceMapAdapter<>(core,
100+
this::resolveBuildRef, this::getNumberOf, this::getBuildClass) {
101+
@Override
102+
protected boolean removeValue(R value) {
103+
return AbstractLazyLoadRunMap.this.removeValue(value);
104+
}
105+
};
109106

110107
@Override
111108
public Set<Integer> keySet() {
112-
Set<Integer> ks = keySet;
113-
if (ks == null) {
114-
ks = new AbstractSet<>() {
115-
@Override
116-
public Iterator<Integer> iterator() {
117-
return new Iterator() {
118-
private final Iterator<Entry<Integer, R>> it = entrySet().iterator();
119-
120-
@Override
121-
public boolean hasNext() {
122-
return it.hasNext();
123-
}
124-
125-
@Override
126-
public Integer next() {
127-
return it.next().getKey();
128-
}
129-
130-
@Override
131-
public void remove() {
132-
it.remove();
133-
}
134-
};
135-
}
136-
137-
@Override
138-
public Spliterator<Integer> spliterator() {
139-
return new Spliterators.AbstractIntSpliterator(
140-
Long.MAX_VALUE,
141-
Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.SORTED) {
142-
private final Iterator<Integer> it = iterator();
143-
144-
@Override
145-
public boolean tryAdvance(IntConsumer action) {
146-
if (action == null) {
147-
throw new NullPointerException();
148-
}
149-
if (it.hasNext()) {
150-
action.accept(it.next());
151-
return true;
152-
}
153-
return false;
154-
}
155-
156-
@Override
157-
public Comparator<Integer> getComparator() {
158-
return Collections.reverseOrder();
159-
}
160-
};
161-
}
162-
163-
@Override
164-
public int size() {
165-
return AbstractLazyLoadRunMap.this.size();
166-
}
167-
168-
@Override
169-
public boolean isEmpty() {
170-
return AbstractLazyLoadRunMap.this.isEmpty();
171-
}
172-
173-
@Override
174-
public void clear() {
175-
AbstractLazyLoadRunMap.this.clear();
176-
}
177-
178-
@Override
179-
public boolean contains(Object k) {
180-
return AbstractLazyLoadRunMap.this.containsKey(k);
181-
}
182-
};
183-
keySet = ks;
184-
}
185-
return ks;
109+
return adapter.keySet();
186110
}
187111

188112
@Override
189113
public Collection<R> values() {
190-
Collection<R> vals = values;
191-
if (vals == null) {
192-
vals = new AbstractCollection<>() {
193-
@Override
194-
public Iterator<R> iterator() {
195-
return new Iterator<>() {
196-
private final Iterator<Entry<Integer, R>> it = entrySet().iterator();
197-
198-
@Override
199-
public boolean hasNext() {
200-
return it.hasNext();
201-
}
202-
203-
@Override
204-
public R next() {
205-
return it.next().getValue();
206-
}
207-
208-
@Override
209-
public void remove() {
210-
it.remove();
211-
}
212-
};
213-
}
214-
215-
@Override
216-
public Spliterator<R> spliterator() {
217-
return Spliterators.spliteratorUnknownSize(
218-
iterator(), Spliterator.DISTINCT | Spliterator.ORDERED);
219-
}
220-
221-
@Override
222-
public int size() {
223-
return AbstractLazyLoadRunMap.this.size();
224-
}
225-
226-
@Override
227-
public boolean isEmpty() {
228-
return AbstractLazyLoadRunMap.this.isEmpty();
229-
}
230-
231-
@Override
232-
public void clear() {
233-
AbstractLazyLoadRunMap.this.clear();
234-
}
114+
return adapter.values();
115+
}
235116

236-
@Override
237-
public boolean contains(Object v) {
238-
return AbstractLazyLoadRunMap.this.containsValue(v);
239-
}
240-
};
241-
values = vals;
242-
}
243-
return vals;
117+
@Override
118+
public Set<Entry<Integer, R>> entrySet() {
119+
assert baseDirInitialized();
120+
return adapter.entrySet();
244121
}
245122

246123
/**
@@ -354,18 +231,22 @@ public final void recognizeNumber(int buildNumber) {
354231

355232
@Override
356233
public Comparator<? super Integer> comparator() {
357-
return Collections.reverseOrder();
234+
return core.comparator();
358235
}
359236

360237
@Override
361238
public boolean isEmpty() {
362-
return search(Integer.MAX_VALUE, DESC) == null;
239+
return adapter.isEmpty();
363240
}
364241

365242
@Override
366-
public Set<Entry<Integer, R>> entrySet() {
367-
assert baseDirInitialized();
368-
return entrySet;
243+
public boolean containsKey(Object value) {
244+
return adapter.containsKey(value);
245+
}
246+
247+
@Override
248+
public boolean containsValue(Object value) {
249+
return adapter.containsValue(value);
369250
}
370251

371252
/**
@@ -379,7 +260,7 @@ public SortedMap<Integer, R> getLoadedBuilds() {
379260
res.put(entry.getKey(), buildRef);
380261
}
381262
}
382-
return Collections.unmodifiableSortedMap(new BuildReferenceMapAdapter<>(this, res));
263+
return new BuildReferenceMapAdapter<>(res, this::resolveBuildRef, this::getNumberOf, this::getBuildClass);
383264
}
384265

385266
/**
@@ -390,33 +271,17 @@ public SortedMap<Integer, R> getLoadedBuilds() {
390271
*/
391272
@Override
392273
public SortedMap<Integer, R> subMap(Integer fromKey, Integer toKey) {
393-
// TODO: if this method can produce a lazy map, that'd be wonderful
394-
// because due to the lack of floor/ceil/higher/lower kind of methods
395-
// to look up keys in SortedMap, various places of Jenkins rely on
396-
// subMap+firstKey/lastKey combo.
397-
398-
R start = search(fromKey, DESC);
399-
if (start == null) return EMPTY_SORTED_MAP;
400-
401-
R end = search(toKey, ASC);
402-
if (end == null) return EMPTY_SORTED_MAP;
403-
404-
for (R i = start; i != end; ) {
405-
i = search(getNumberOf(i) - 1, DESC);
406-
assert i != null;
407-
}
408-
409-
return Collections.unmodifiableSortedMap(new BuildReferenceMapAdapter<>(this, core.subMap(fromKey, toKey)));
274+
return adapter.subMap(fromKey, toKey);
410275
}
411276

412277
@Override
413278
public SortedMap<Integer, R> headMap(Integer toKey) {
414-
return subMap(Integer.MAX_VALUE, toKey);
279+
return adapter.headMap(toKey);
415280
}
416281

417282
@Override
418283
public SortedMap<Integer, R> tailMap(Integer fromKey) {
419-
return subMap(fromKey, Integer.MIN_VALUE);
284+
return adapter.tailMap(fromKey);
420285
}
421286

422287
@Override
@@ -443,11 +308,7 @@ public R oldestBuild() {
443308

444309
@Override
445310
public R get(Object key) {
446-
if (key instanceof Integer) {
447-
int n = (Integer) key;
448-
return get(n);
449-
}
450-
return super.get(key);
311+
return adapter.get(key);
451312
}
452313

453314
public R get(int n) {
@@ -552,7 +413,7 @@ private R resolveBuildRef(BuildReference<R> ref) {
552413
}
553414

554415
public R getByNumber(int n) {
555-
return resolveBuildRef(core.get(n));
416+
return adapter.get(n);
556417
}
557418

558419
/**
@@ -597,14 +458,9 @@ public synchronized void putAll(Map<? extends Integer, ? extends R> newData) {
597458
core.putAll(newWrapperData);
598459
}
599460

600-
/**
601-
* Return underlining {@link BuildReference} core map.
602-
*
603-
* @return
604-
* full build reference map.
605-
*/
606-
/*package*/ SortedMap<Integer, BuildReference<R>> all() {
607-
return core;
461+
@Override
462+
public R remove(Object key) {
463+
return adapter.remove(key);
608464
}
609465

610466
/**
@@ -652,7 +508,6 @@ protected BuildReference<R> createReference(R r) {
652508
return new BuildReference<>(getIdOf(r), r);
653509
}
654510

655-
656511
/**
657512
* Parses {@code R} instance from data in the specified directory.
658513
*
@@ -664,6 +519,8 @@ protected BuildReference<R> createReference(R r) {
664519
*/
665520
protected abstract R retrieve(File dir) throws IOException;
666521

522+
protected abstract Class<R> getBuildClass();
523+
667524
public synchronized boolean removeValue(R run) {
668525
return core.remove(getNumberOf(run)) != null;
669526
}
@@ -694,7 +551,5 @@ public enum Direction {
694551
ASC, DESC, EXACT
695552
}
696553

697-
private static final SortedMap EMPTY_SORTED_MAP = Collections.unmodifiableSortedMap(new TreeMap());
698-
699554
static final Logger LOGGER = Logger.getLogger(AbstractLazyLoadRunMap.class.getName());
700555
}

0 commit comments

Comments
 (0)