package net.johnewart.gearman.client;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import net.johnewart.gearman.common.Job;
import net.johnewart.gearman.common.JobState;
import net.johnewart.gearman.common.JobStatus;
import net.johnewart.gearman.common.events.WorkEvent;
import net.johnewart.gearman.common.packets.response.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.johnewart.gearman.common.interfaces.GearmanFunction;
import net.johnewart.gearman.common.interfaces.GearmanWorker;
import net.johnewart.gearman.common.packets.Packet;
import net.johnewart.gearman.common.packets.request.CanDo;
import net.johnewart.gearman.common.packets.request.GrabJob;
import net.johnewart.gearman.common.packets.request.PreSleep;
import net.johnewart.gearman.constants.PacketType;
import net.johnewart.gearman.net.Connection;
import net.johnewart.gearman.net.ConnectionPool;
public class NetworkGearmanWorker implements GearmanWorker, Runnable {
private final ConnectionPool connectionPool;
private final Map<String, GearmanFunction> callbacks;
private final AtomicBoolean isActive;
private static Logger LOG = LoggerFactory.getLogger(NetworkGearmanWorker.class);
private final Map<Job, Connection> jobConnectionMap;
private NetworkGearmanWorker()
{
this.connectionPool = new ConnectionPool();
this.callbacks = new HashMap<>();
this.isActive = new AtomicBoolean(true);
this.jobConnectionMap = new ConcurrentHashMap<>();
}
private NetworkGearmanWorker(NetworkGearmanWorker other)
{
this.connectionPool = other.connectionPool;
this.callbacks = other.callbacks;
this.jobConnectionMap = other.jobConnectionMap;
this.isActive = new AtomicBoolean(true);
}
@Override
public void registerCallback(String method, GearmanFunction function)
{
callbacks.put(method, function);
broadcastAbility(method);
}
@Override
public void doWork()
{
while(isActive.get()) {
for(Connection c : connectionPool.getGoodConnectionList()) {
LOG.debug("Trying " + c.toString());
Job nextJob = null;
try {
c.sendPacket(new GrabJob());
Packet p = c.getNextPacket();
byte[] result;
switch(p.getType()) {
case JOB_ASSIGN:
JobAssign jobAssign = (JobAssign)p;
nextJob = jobAssign.getJob();
break;
case JOB_ASSIGN_UNIQ:
JobAssignUniq jobAssignUniq = (JobAssignUniq)p;
nextJob = jobAssignUniq.getJob();
break;
case NO_JOB:
LOG.info("Worker sending PRE_SLEEP and sleeping for 30 seconds...");
c.sendPacket(new PreSleep());
try {
Packet noop = c.getNextPacket(30 * 1000);
if(noop.getType() != PacketType.NOOP) {
LOG.error("Received invalid packet. Expected NOOP, received " + noop.getType());
}
} catch (SocketTimeoutException e) {
LOG.warn("Socket timed out waiting for next packet...");
}
break;
}
if (nextJob != null) {
jobConnectionMap.put(nextJob, c);
WorkEvent workEvent = new WorkEvent(nextJob, this);
result = callbacks.get(nextJob.getFunctionName()).process(workEvent);
c.sendPacket(new WorkCompleteResponse(nextJob.getJobHandle(), result));
}
} catch (IOException ioe) {
LOG.error("I/O error: ", ioe);
} finally {
if (nextJob != null)
jobConnectionMap.remove(nextJob);
}
}
}
LOG.debug("Worker has been stopped.");
}
@Override
public void stopWork() {
LOG.debug("Stopping network gearman worker.");
isActive.set(false);
this.connectionPool.shutdown();
}
@Override
public void sendData(Job job, byte[] data) throws IOException {
WorkDataResponse workDataResponse = new WorkWarningResponse(job.getJobHandle(), data);
Connection c = jobConnectionMap.get(job);
c.sendPacket(workDataResponse);
LOG.debug("Sent data with " + data.length + " bytes to " + c);
}
@Override
public void sendStatus(Job job, int numerator, int denominator) throws IOException {
JobStatus jobStatus = new JobStatus(numerator, denominator, JobState.WORKING, job.getJobHandle());
Connection c = jobConnectionMap.get(job);
c.sendPacket(new WorkStatus(jobStatus));
LOG.debug("Sent work status of " + jobStatus + " to " + c);
}
@Override
public void sendWarning(Job job, byte[] warning) throws IOException {
WorkWarningResponse workWarningResponse = new WorkWarningResponse(job.getJobHandle(), warning);
Connection c = jobConnectionMap.get(job);
c.sendPacket(workWarningResponse);
LOG.debug("Sent warning with " + warning.length + " bytes to " + c);
}
private void broadcastAbility(String functionName)
{
for(Connection c : connectionPool.getGoodConnectionList())
{
try {
c.sendPacket(new CanDo(functionName));
} catch (IOException e) {
LOG.error("IO Exception: ", e);
}
}
}
@Override
public void run() {
//To change body of implemented methods use File | Settings | File Templates.
}
public static class Builder {
private NetworkGearmanWorker worker;
public Builder() {
this.worker = new NetworkGearmanWorker();
}
public NetworkGearmanWorker build() {
return new NetworkGearmanWorker(worker);
}
public Builder withConnection(Connection c) {
worker.connectionPool.addConnection(c);
return this;
}
public Builder withHostPort(String host, int port) {
worker.connectionPool.addHostPort(host, port);
return this;
}
}
}