gearman-plugin/src/main/java/hudson/plugins/gearman/NodeAvailabilityMonitor.java

144 lines
4.9 KiB
Java

/*
*
* Copyright 2013 OpenStack Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package hudson.plugins.gearman;
import jenkins.model.Jenkins;
import hudson.model.Queue;
import hudson.model.Computer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NodeAvailabilityMonitor implements AvailabilityMonitor {
private final Queue queue;
private final Jenkins jenkins;
private final Computer computer;
private MyGearmanWorkerImpl workerHoldingLock = null;
private String expectedUUID = null;
private static final Logger logger = LoggerFactory
.getLogger(Constants.PLUGIN_LOGGER_NAME);
NodeAvailabilityMonitor(Computer computer)
{
this.computer = computer;
queue = Queue.getInstance();
jenkins = Jenkins.getInstance();
}
public Computer getComputer() {
return computer;
}
public void lock(MyGearmanWorkerImpl worker)
throws InterruptedException
{
logger.debug("AvailabilityMonitor lock request: " + worker);
while (true) {
boolean busy = false;
// Synchronize on the Jenkins queue so that Jenkins is
// unable to schedule builds while we try to acquire the
// lock.
synchronized(queue) {
if (workerHoldingLock == null) {
if (computer.countIdle() == 0) {
// If there are no idle executors, we can not
// schedule a build.
busy = true;
} else if (jenkins.isQuietingDown()) {
busy = true;
} else {
logger.debug("AvailabilityMonitor got lock: " + worker);
workerHoldingLock = worker;
return;
}
} else {
busy = true;
}
}
if (busy) {
synchronized(this) {
// We get synchronous notification when a
// build finishes, but there are lots of other
// reasons circumstances could change (adding
// an executor, canceling shutdown, etc), so
// we slowly busy wait to cover all those
// reasons.
this.wait(5000);
}
}
}
}
public void unlock(MyGearmanWorkerImpl worker) {
logger.debug("AvailabilityMonitor unlock request: " + worker);
synchronized(queue) {
if (workerHoldingLock == worker) {
workerHoldingLock = null;
expectedUUID = null;
logger.debug("AvailabilityMonitor unlocked: " + worker);
} else {
logger.debug("Worker does not own AvailabilityMonitor lock: " +
worker);
}
}
wake();
}
public void wake() {
// Called when we know circumstances may have changed in a way
// that may allow someone to get the lock.
logger.debug("AvailabilityMonitor wake request");
synchronized(this) {
logger.debug("AvailabilityMonitor woken");
notifyAll();
}
}
public void expectUUID(String UUID) {
// The Gearman worker which holds the lock is about to
// schedule this build, so when Jenkins asks to run it, say
// "yes".
if (expectedUUID != null) {
logger.error("AvailabilityMonitor told to expect UUID " +
UUID + "while already expecting " + expectedUUID);
}
expectedUUID = UUID;
}
public boolean canTake(Queue.BuildableItem item)
{
// Jenkins calls this from within the scheduler maintenance
// function (while owning the queue monitor). If we are
// locked, only allow the build we are expecting to run.
logger.debug("AvailabilityMonitor canTake request for " +
workerHoldingLock);
NodeParametersAction param = item.getAction(NodeParametersAction.class);
if (param != null) {
logger.debug("AvailabilityMonitor canTake request for UUID " +
param.getUuid() + " expecting " + expectedUUID);
if (expectedUUID == param.getUuid()) {
return true;
}
}
return (workerHoldingLock == null);
}
}