Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-43507] Make more lambda friendly
  • Loading branch information
stephenc committed May 4, 2017
1 parent a3485be commit fd479ad
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 62 deletions.
104 changes: 92 additions & 12 deletions src/main/java/jenkins/scm/api/trait/SCMSourceRequest.java
Expand Up @@ -36,6 +36,7 @@
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMProbe;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.SCMSourceCriteria;
Expand All @@ -61,7 +62,7 @@ public abstract class SCMSourceRequest implements Closeable {

private final Set<SCMHead> observerIncludes;

protected SCMSourceRequest(SCMSourceRequestBuilder<?,?> builder) {
protected SCMSourceRequest(SCMSourceRequestBuilder<?, ?> builder) {
this.filters = builder.filters();
this.criteria = builder.criteria().isEmpty()
? Collections.<SCMSourceCriteria>emptyList()
Expand Down Expand Up @@ -91,21 +92,84 @@ public List<SCMSourceCriteria> getCriteria() {
return criteria;
}

public boolean hasNoCriteria() {
return criteria.isEmpty();
/**
* Processes a head in the context of the current request.
*
* @param head the {@link SCMHead} to process.
* @param revisionFactory factory method that creates the {@link SCMRevision} (assuming creation is cheap).
* @param probeFactory factory method that creates the {@link SCMProbe}.
* @param <H> the type of {@link SCMHead}.
* @param <R> the type of {@link SCMRevision}.
* @return {@code true} if the {@link SCMHeadObserver} for this request has completed observing, {@code false} to
* continue processing.
* @throws IOException if there was an I/O error.
* @throws InterruptedException if the processing was interrupted.
*/
public <H extends SCMHead, R extends SCMRevision> boolean process(final H head,
final RevisionFactory<H, R> revisionFactory,
ProbeFactory<H, R> probeFactory)
throws IOException, InterruptedException {
return process(head, new IntermediateFactory<R>() {
@Override
public R create() throws IOException, InterruptedException {
return revisionFactory.create(head);
}
}, probeFactory, new LazyRevisionFactory<H, SCMRevision, R>() {
@Override
public SCMRevision create(H head, R intermediate) throws IOException, InterruptedException {
return intermediate;
}
});
}

public boolean meetsCriteria(SCMSourceCriteria.Probe probe) throws IOException {
for (SCMSourceCriteria c : criteria) {
if (!c.isHead(probe, listener)) {
return false;
/**
* Processes a head in the context of the current request where an intermediary operation is required before
* the {@link SCMRevision} can be instantiated.
*
* @param head the {@link SCMHead} to process.
* @param intermediateFactory factory method that provides the seed information for both the {@link ProbeFactory}
* and the {@link LazyRevisionFactory}.
* @param probeFactory factory method that creates the {@link SCMProbe}.
* @param revisionFactory factory method that creates the {@link SCMRevision}.
* @param <H> the type of {@link SCMHead}.
* @param <I> the type of the intermediary operation result.
* @param <R> the type of {@link SCMRevision}.
* @return {@code true} if the {@link SCMHeadObserver} for this request has completed observing, {@code false} to
* continue processing.
* @throws IOException if there was an I/O error.
* @throws InterruptedException if the processing was interrupted.
*/
public <H extends SCMHead, I, R extends SCMRevision> boolean process(H head,
IntermediateFactory<I> intermediateFactory,
ProbeFactory<H, I> probeFactory,
LazyRevisionFactory<H, R, I> revisionFactory)
throws IOException, InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (isExcluded(head)) {
// not included
return !observer.isObserving();
}
I intermediate = intermediateFactory.create();
if (!criteria.isEmpty()) {
SCMSourceCriteria.Probe probe = probeFactory.create(head, intermediate);
try {
for (SCMSourceCriteria c : criteria) {
if (!c.isHead(probe, listener)) {
// not a match against criteria
return !observer.isObserving();
}
}
} finally {
if (probe instanceof Closeable) {
((Closeable) probe).close();
}
}
}
return true;
}

public void criteriaMet(SCMHead head, SCMRevision revision) throws IOException, InterruptedException {
observer.observe(head, revision);
// observe
observer.observe(head, revisionFactory.create(head, intermediate));
return !observer.isObserving();
}

public boolean isComplete() {
Expand All @@ -123,4 +187,20 @@ public TaskListener listener() {
public void close() throws IOException {
// default to no-op but allow subclasses to store persistent connections in the request and clean up after
}

public interface RevisionFactory<H extends SCMHead, R extends SCMRevision> {
R create(H head) throws IOException, InterruptedException;
}

public interface ProbeFactory<H extends SCMHead, I> {
SCMSourceCriteria.Probe create(H head, I revision) throws IOException, InterruptedException;
}

public interface LazyRevisionFactory<H extends SCMHead, R extends SCMRevision, I> {
R create(H head, I intermediate) throws IOException, InterruptedException;
}

public interface IntermediateFactory<I> {
I create() throws IOException, InterruptedException;
}
}
Expand Up @@ -33,7 +33,7 @@ public static final class DescriptorImpl extends SCMSourceTraitDescriptor {

@Override
public String getDisplayName() {
return "";
return "Discover branches";
}

@Override
Expand Down
Expand Up @@ -74,7 +74,7 @@ public static final class DescriptorImpl extends SCMSourceTraitDescriptor {

@Override
public String getDisplayName() {
return "";
return "Discover change requests";
}

@Override
Expand Down
Expand Up @@ -34,7 +34,7 @@ public static final class DescriptorImpl extends SCMSourceTraitDescriptor {

@Override
public String getDisplayName() {
return "";
return "Discover tags";
}

@Override
Expand Down
111 changes: 64 additions & 47 deletions src/test/java/jenkins/scm/impl/mock/MockSCMSource.java
Expand Up @@ -58,6 +58,7 @@
import jenkins.scm.api.metadata.ContributorMetadataAction;
import jenkins.scm.api.metadata.ObjectMetadataAction;
import jenkins.scm.api.mixin.ChangeRequestCheckoutStrategy;
import jenkins.scm.api.trait.SCMSourceRequest;
import jenkins.scm.api.trait.SCMSourceTrait;
import jenkins.scm.api.trait.SCMSourceTraitDescriptor;
import jenkins.scm.impl.ChangeRequestSCMHeadCategory;
Expand Down Expand Up @@ -129,71 +130,87 @@ protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHe
controller().checkFaults(repository, null, null, false);
if (request.isFetchBranches()) {
for (final String branch : controller().listBranches(repository)) {
checkInterrupt();
String revision = controller().getRevision(repository, branch);
MockSCMHead head = new MockSCMHead(branch);
if (request.isExcluded(head)) {
continue;
}
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
if (request.hasNoCriteria() || request.meetsCriteria(new MockSCMProbe(head, revision))) {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision, false);
request.criteriaMet(head, new MockSCMRevision(head, revision));
}
if (request.isComplete()) {
if (request.process(new MockSCMHead(branch),
new SCMSourceRequest.RevisionFactory<MockSCMHead, MockSCMRevision>() {
@Override
public MockSCMRevision create(MockSCMHead head) throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
return new MockSCMRevision(head, controller().getRevision(repository, branch));
}
}, new SCMSourceRequest.ProbeFactory<MockSCMHead, MockSCMRevision>() {
@Override
public SCMSourceCriteria.Probe create(MockSCMHead head, MockSCMRevision revision) throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision.getHash(), false);
return new MockSCMProbe(head, revision.getHash());
}
})) {
return;
}
}
}
if (request.isFetchTags()) {
for (final String tag : controller().listTags(repository)) {
checkInterrupt();
String revision = controller().getRevision(repository, tag);
MockSCMHead head = new MockTagSCMHead(tag, controller().getTagTimestamp(repository, tag));
if (request.isExcluded(head)) {
continue;
}
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
if (request.hasNoCriteria() || request.meetsCriteria(new MockSCMProbe(head, revision))) {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision, false);
request.criteriaMet(head, new MockSCMRevision(head, revision));
}
if (request.isComplete()) {
if (request.process(new MockTagSCMHead(tag, controller().getTagTimestamp(repository, tag)),
new SCMSourceRequest.RevisionFactory<MockTagSCMHead, MockSCMRevision>() {
@Override
public MockSCMRevision create(MockTagSCMHead head)
throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
return new MockSCMRevision(head, controller().getRevision(repository, tag));
}
}, new SCMSourceRequest.ProbeFactory<MockTagSCMHead, MockSCMRevision>() {
@Override
public SCMSourceCriteria.Probe create(MockTagSCMHead head, MockSCMRevision revision)
throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision.getHash(), false);
return new MockSCMProbe(head, revision.getHash());
}
})) {
return;
}
}
}
if (request.isFetchChangeRequests()) {
for (final Integer number : controller().listChangeRequests(repository)) {
checkInterrupt();
String revision = controller().getRevision(repository, "change-request/" + number);
String target = controller().getTarget(repository, number);
String targetRevision = controller().getRevision(repository, target);
Set<MockChangeRequestFlags> crFlags = controller.getFlags(repository, number);
Set<ChangeRequestCheckoutStrategy> strategies = request.getCheckoutStrategies();
boolean singleStrategy = strategies.size() == 1;
for (ChangeRequestCheckoutStrategy strategy : strategies) {
MockChangeRequestSCMHead head = new MockChangeRequestSCMHead(
crFlags.contains(MockChangeRequestFlags.FORK)
? new SCMHeadOrigin.Fork("fork")
: null,
number, target, strategy, singleStrategy);
if (request.isExcluded(head)) {
continue;
}
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
if (request.hasNoCriteria() || request.meetsCriteria(new MockSCMProbe(head, revision))) {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision, false);
request.criteriaMet(head, new MockChangeRequestSCMRevision(head,
new MockSCMRevision(head.getTarget(), targetRevision), revision));
}
if (request.isComplete()) {
if (request.process(new MockChangeRequestSCMHead(
crFlags.contains(MockChangeRequestFlags.FORK)
? new SCMHeadOrigin.Fork("fork")
: null,
number, target, strategy, singleStrategy),
new SCMSourceRequest.RevisionFactory<MockChangeRequestSCMHead, MockChangeRequestSCMRevision>() {
@Override
public MockChangeRequestSCMRevision create(MockChangeRequestSCMHead head)
throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), null, false);
String revision =
controller().getRevision(repository, "change-request/" + number);
String targetRevision =
controller().getRevision(repository, head.getTarget().getName());
return new MockChangeRequestSCMRevision(head,
new MockSCMRevision(head.getTarget(), targetRevision), revision);
}
},
new SCMSourceRequest.ProbeFactory<MockChangeRequestSCMHead, MockChangeRequestSCMRevision>() {
@Override
public SCMSourceCriteria.Probe create(MockChangeRequestSCMHead head,
MockChangeRequestSCMRevision revision)
throws IOException, InterruptedException {
controller().applyLatency();
controller().checkFaults(repository, head.getName(), revision.getHash(), false);
return new MockSCMProbe(head, revision.getHash());
}
})) {
return;
}
}
Expand Down

0 comments on commit fd479ad

Please sign in to comment.