Skip to content

Commit 8ba85d8

Browse files
author
Dmytro Ukhlov
committed
Fix NullPointerException in LazyBuildMixIn on jenkins reload
1 parent c696563 commit 8ba85d8

File tree

2 files changed

+26
-76
lines changed

2 files changed

+26
-76
lines changed

core/src/main/java/hudson/tasks/LogRotator.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,12 @@ public void perform(Job<?, ?> job) throws IOException, InterruptedException {
163163
Run lsb = removeLastBuild ? null : job.getLastSuccessfulBuild();
164164
Run lstb = removeLastBuild ? null : job.getLastStableBuild();
165165

166+
Map<Integer, ? extends Run<?, ?>> runMap = job.getBuildsAsMap();
166167
if (numToKeep != -1) {
167-
job.getBuildsAsMap().entrySet().stream().skip(numToKeep).map(Map.Entry::getValue)
168-
.filter(r -> !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
168+
// Iterate through keySet() instead of entrySet() or values() to avoid triggering lazy loading
169+
// for the first `numToKeep` builds
170+
runMap.keySet().stream().skip(numToKeep).map(runMap::get)
171+
.filter(r -> r != null && !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
169172
LOGGER.log(FINE, "{0} is to be removed", r);
170173
try { r.delete(); }
171174
catch (IOException ex) { exceptionMap.computeIfAbsent(r, key -> new HashSet<>()).add(ex); }
@@ -190,8 +193,10 @@ public void perform(Job<?, ?> job) throws IOException, InterruptedException {
190193
}
191194

192195
if (artifactNumToKeep != null && artifactNumToKeep != -1) {
193-
job.getBuildsAsMap().entrySet().stream().skip(artifactNumToKeep).map(Map.Entry::getValue)
194-
.filter(r -> !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
196+
// Iterate through keySet() instead of entrySet() or values() to avoid triggering lazy loading
197+
// for the first `artifactNumToKeep` builds
198+
runMap.keySet().stream().skip(artifactNumToKeep).map(runMap::get)
199+
.filter(r -> r != null && !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
195200
LOGGER.log(FINE, "{0} is to be purged of artifacts", r);
196201
try { r.deleteArtifacts(); }
197202
catch (IOException ex) { exceptionMap.computeIfAbsent(r, key -> new HashSet<>()).add(ex); }

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

Lines changed: 17 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,20 @@ public boolean remove(Object o) {
169169

170170
@Override
171171
public Iterator<Integer> iterator() {
172-
return new AdaptedIterator<>(BuildReferenceMapAdapter.this.entrySet().iterator()) {
173-
@Override
174-
protected Integer adapt(Map.Entry<Integer, R> e) {
175-
return e.getKey();
176-
}
177-
};
172+
return Iterators.removeNull(Iterators.map(
173+
BuildReferenceMapAdapter.this.core.entrySet().iterator(), coreEntry -> {
174+
BuildReference<R> ref = coreEntry.getValue();
175+
if (!ref.isSet()) {
176+
R r = resolver.resolveBuildRef(ref);
177+
if (r == null) {
178+
return null;
179+
}
180+
}
181+
if (ref.isUnloadable()) {
182+
return null;
183+
}
184+
return ref.number;
185+
}));
178186
}
179187

180188
@Override
@@ -273,19 +281,9 @@ public Iterator<Map.Entry<Integer, R>> iterator() {
273281
private Map.Entry<Integer, R> current;
274282
private final Iterator<Map.Entry<Integer, R>> it = Iterators.removeNull(Iterators.map(
275283
BuildReferenceMapAdapter.this.core.entrySet().iterator(), coreEntry -> {
276-
BuildReference<R> ref = coreEntry.getValue();
277-
if (!ref.isSet()) {
278-
R r = resolver.resolveBuildRef(ref);
279-
// load not loaded or unloadable build
280-
if (r == null) {
281-
return null;
282-
}
283-
return new EntryAdapter(coreEntry, r);
284-
}
285-
if (ref.isUnloadable()) {
286-
return null;
287-
}
288-
return new EntryAdapter(coreEntry);
284+
R r = resolver.resolveBuildRef(coreEntry.getValue());
285+
// if null - load not allowed or build is unloadable
286+
return r == null ? null : new AbstractMap.SimpleImmutableEntry<>(coreEntry.getKey(), r);
289287
}));
290288

291289
@Override
@@ -314,59 +312,6 @@ public Spliterator<Map.Entry<Integer, R>> spliterator() {
314312
}
315313
}
316314

317-
private class EntryAdapter implements Entry<Integer, R> {
318-
private final Map.Entry<Integer, BuildReference<R>> coreEntry;
319-
private volatile R resolvedValue;
320-
321-
EntryAdapter(Map.Entry<Integer, BuildReference<R>> coreEntry) {
322-
this(coreEntry, null);
323-
}
324-
325-
EntryAdapter(Map.Entry<Integer, BuildReference<R>> coreEntry, R resolvedValue) {
326-
this.coreEntry = coreEntry;
327-
this.resolvedValue = resolvedValue;
328-
}
329-
330-
private Map.Entry<Integer, R> getResolvedEntry() {
331-
return new AbstractMap.SimpleEntry<>(getKey(), getValue());
332-
}
333-
334-
@Override
335-
public Integer getKey() {
336-
return coreEntry.getKey();
337-
}
338-
339-
@Override
340-
public R getValue() {
341-
R value = resolvedValue;
342-
if (value != null) {
343-
return value;
344-
}
345-
return resolvedValue = resolver.resolveBuildRef(coreEntry.getValue());
346-
}
347-
348-
@Override
349-
public R setValue(R value) {
350-
// BuildReferenceAdapter is read only
351-
throw new UnsupportedOperationException();
352-
}
353-
354-
@Override
355-
public String toString() {
356-
return getResolvedEntry().toString();
357-
}
358-
359-
@Override
360-
public boolean equals(Object o) {
361-
return (o instanceof Map.Entry<?, ?>) && getResolvedEntry().equals(o);
362-
}
363-
364-
@Override
365-
public int hashCode() {
366-
return getResolvedEntry().hashCode();
367-
}
368-
}
369-
370315
/**
371316
* An interface for resolving build references into actual build instances
372317
* and extracting basic metadata from them.

0 commit comments

Comments
 (0)