Skip to content

Commit

Permalink
[FIXED JENKINS-19400] Configurable retention system for lazy-loaded b…
Browse files Browse the repository at this point in the history
…uild references.
  • Loading branch information
jglick committed Dec 10, 2013
1 parent 9679a61 commit 89d7570
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 6 deletions.
Expand Up @@ -33,7 +33,6 @@
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.ref.Reference;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -577,7 +576,7 @@ public synchronized R put(Integer key, R r) {
return unwrap(old);
}

private R unwrap(Reference<R> ref) {
private R unwrap(BuildReference<R> ref) {
return ref!=null ? ref.get() : null;
}

Expand Down
139 changes: 135 additions & 4 deletions core/src/main/java/jenkins/model/lazy/BuildReference.java
@@ -1,9 +1,21 @@
package jenkins.model.lazy;

import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.model.Run;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;

/**
* {@link SoftReference} to a build object.
* Reference (by default a {@link SoftReference}) to a build object.
*
* <p>
* To be able to re-retrieve the referent in case it is lost, this class
Expand All @@ -15,14 +27,27 @@
* and find things in it.
*
* @author Kohsuke Kawaguchi
* @since 1.485
* @since 1.485 (but as of TODO not a {@link SoftReference})
*/
public final class BuildReference<R> extends SoftReference<R> {
public final class BuildReference<R> {

private static final Logger LOGGER = Logger.getLogger(BuildReference.class.getName());

final String id;
private final Holder<R> holder;

public BuildReference(String id, R referent) {
super(referent);
this.id = id;
this.holder = findHolder(referent);
}

/**
* Gets the build if still in memory.
* @return the actual build, or null if it has been collected
* @see Holder#get
*/
public @CheckForNull R get() {
return holder.get();
}

@Override
Expand All @@ -39,4 +64,110 @@ public boolean equals(Object o) {
public int hashCode() {
return id.hashCode();
}

/**
* An abstraction of {@link Reference}.
* @since TODO
*/
public interface Holder<R> {

/**
* Gets a build.
* @return the build reference, or null if collected
*/
@CheckForNull R get();

}

/**
* Extensible factory for creating build references.
* @since TODO
*/
public interface HolderFactory extends ExtensionPoint {

/**
* Constructs a single build reference.
* @param <R> the type of thing (generally {@link Run})
* @param referent the thing to load
* @return a reference, or null to consult the next factory
*/
@CheckForNull <R> Holder<R> make(@Nonnull R referent);

}

private static <R> Holder<R> findHolder(R referent) {
if (referent == null) {
// AbstractBuild.NONE
return new DefaultHolderFactory.NoHolder<R>();
}
for (HolderFactory f : Jenkins.getInstance().getExtensionList(HolderFactory.class)) {
Holder<R> h = f.make(referent);
if (h != null) {
LOGGER.log(Level.FINE, "created build reference for {0} using {1}", new Object[] {referent, f});
return h;
}
}
throw new IllegalStateException();
}

/**
* Default factory if none other are installed.
* Its behavior can be controlled via the system property {@link DefaultHolderFactory#MODE_PROPERTY}:
* <dl>
* <dt><code>soft</code> (default)
* <dd>Use {@link SoftReference}s. Builds will be kept around so long as memory pressure is not too high.
* <dt><code>weak</code>
* <dd>Use {@link WeakReference}s. Builds will be kept only until the next full garbage collection cycle.
* <dt><code>strong</code>
* <dd>Use strong references. Builds will still be loaded lazily, but once loaded, will not be released.
* <dt><code>none</code>
* <dd>Do not hold onto builds at all. Mainly offered as an option for the purpose of reproducing lazy-loading bugs.
* </dl>
*/
@Restricted(DoNotUse.class)
@Extension(ordinal=Double.NEGATIVE_INFINITY) public static final class DefaultHolderFactory implements HolderFactory {

public static final String MODE_PROPERTY = "jenkins.model.lazy.BuildReference.MODE";
private static final String mode = System.getProperty(MODE_PROPERTY);

@Override public <R> Holder<R> make(R referent) {
if (mode == null || mode.equals("soft")) {
return new SoftHolder<R>(referent);
} else if (mode.equals("weak")) {
return new WeakHolder<R>(referent);
} else if (mode.equals("strong")) {
return new StrongHolder<R>(referent);
} else if (mode.equals("none")) {
return new NoHolder<R>();
} else {
throw new IllegalStateException("unrecognized value of " + MODE_PROPERTY + ": " + mode);
}
}

private static final class SoftHolder<R> extends SoftReference<R> implements Holder<R> {
SoftHolder(R referent) {
super(referent);
}
}

private static final class WeakHolder<R> extends WeakReference<R> implements Holder<R> {
WeakHolder(R referent) {
super(referent);
}
}

private static final class StrongHolder<R> implements Holder<R> {
private final R referent;
StrongHolder(R referent) {
this.referent = referent;
}
@Override public R get() {return referent;}
}

private static final class NoHolder<R> implements Holder<R> {
@Override public R get() {return null;}
}

}

}

0 comments on commit 89d7570

Please sign in to comment.