/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.work; import divconq.hub.Hub; import divconq.hub.HubState; import divconq.hub.ISystemWork; import divconq.hub.SysReporter; import divconq.lang.op.FuncResult; import divconq.lang.op.OperationResult; import divconq.log.Logger; import divconq.struct.ListStruct; import divconq.struct.RecordStruct; import divconq.struct.Struct; import divconq.util.StringUtil; import divconq.xml.XElement; // longer term jobs than work pool - recoverable, retry, debugable, etc public class WorkQueue implements IQueueDriver, IQueueAlerter { protected IQueueDriver impl = null; protected IQueueAlerter alerter = null; @Override public void init(OperationResult or, XElement config) { if (config == null) // no error, it is ok to have a hub without a work queue return; // setup the provider of the work queue String classname = config.getAttribute("InterfaceClass"); if (StringUtil.isEmpty(classname)) { or.errorTr(173); return; } Object impl = Hub.instance.getInstance(classname); if ((impl == null) || !(impl instanceof IQueueDriver)) { or.errorTr(174, classname); return; } this.impl = (IQueueDriver)impl; this.impl.init(or, config); // setup the class to handle alerts classname = config.getAttribute("AlertClass"); if (StringUtil.isNotEmpty(classname)) { impl = Hub.instance.getInstance(classname); if ((impl == null) || !(impl instanceof IQueueAlerter)) { or.errorTr(180, classname); return; } this.alerter = (IQueueAlerter)impl; this.alerter.init(or, config); } ISystemWork queuechecker = new ISystemWork() { @Override public void run(SysReporter reporter) { reporter.setStatus("Reviewing bucket work queues"); if (Hub.instance.getState() != HubState.Running) // only grab work when running return; for (WorkBucket pool : Hub.instance.getWorkPool().getBuckets()) { if (!pool.getAutomaticQueueLoader()) continue; int howmany = pool.availCount(); if (howmany < 1) continue; FuncResult<ListStruct> matches = WorkQueue.this.impl.findPotentialClaims(pool.getName(), howmany); if (matches.hasErrors()) { Logger.warn(matches.getMessage()); continue; } ListStruct rs = matches.getResult(); //System.out.print(rs.getSize() + ""); for (Struct match : rs.getItems()) { RecordStruct rec = (RecordStruct)match; FuncResult<RecordStruct> claimop = WorkQueue.this.impl.makeClaim(rec); // ignore errors, typically means someone else got to it first if (claimop.hasErrors()) continue; // replace rec = claimop.getResult(); FuncResult<Task> loadop = WorkQueue.this.impl.loadWork(rec); // enough. should be logged, skip if (loadop.hasErrors()) continue; // TODO fix dcQueue feature DCTASKLOG so we get the full builder object Task info = loadop.getResult(); // TODO collect task objects here and watch lastActivity to update the claim // when updating claims, also routinely check for and update the logs in the db server? // TODO if being debugged put in session //Hub.instance.getSessions().createForSingleTaskAndDie(info); Hub.instance.getWorkPool().submit(info); } } reporter.setStatus("After bucket work queues"); } @Override public int period() { // every 2 seconds to check for new tasks to claim - TODO config return 2; } }; Hub.instance.getClock().addSlowSystemWorker(queuechecker); } @Override public void start(OperationResult or) { if (this.impl != null) this.impl.start(or); } @Override public void stop(OperationResult or) { if (this.impl != null) this.impl.stop(or); } @Override public FuncResult<ListStruct> findPotentialClaims(String pool, int howmanymax) { if (this.impl != null) return this.impl.findPotentialClaims(pool, howmanymax); FuncResult<ListStruct> or = new FuncResult<ListStruct>(); or.errorTr(172); return or; } @Override public FuncResult<RecordStruct> makeClaim(RecordStruct info) { if (this.impl != null) return this.impl.makeClaim(info); FuncResult<RecordStruct> or = new FuncResult<RecordStruct>(); or.errorTr(172); return or; } @Override public OperationResult updateClaim(Task info) { if (this.impl != null) return this.impl.updateClaim(info); OperationResult or = new OperationResult(); or.errorTr(172); return or; } public FuncResult<String> reserveUniqueAndSubmit(Task task) { FuncResult<String> cres = this.reserveUniqueWork(task.getId()); if (cres.hasErrors()) return cres; // if empty then assume someone else reserved it so skip (return "all is ok") if (cres.isEmptyResult()) return new FuncResult<>(); // we must have a claim, which means no one else can take it task.withClaimedStamp(cres.getResult()); return this.submit(task); } @Override public FuncResult<String> reserveUniqueWork(String taskidentity) { if (this.impl != null) return this.impl.reserveUniqueWork(taskidentity); FuncResult<String> or = new FuncResult<String>(); or.errorTr(172); return or; } public FuncResult<String> reserveCurrentAndSubmit(Task task) { FuncResult<String> cres = this.reserveCurrentWork(task.getId()); if (cres.hasErrors()) return cres; // if empty then assume someone else reserved it so skip (return "all is ok") if (cres.isEmptyResult()) return new FuncResult<>(); // we must have a claim, which means no one else can take it task.withClaimedStamp(cres.getResult()); return this.submit(task); } @Override public FuncResult<String> reserveCurrentWork(String taskidentity) { if (this.impl != null) return this.impl.reserveCurrentWork(taskidentity); FuncResult<String> or = new FuncResult<String>(); or.errorTr(172); return or; } @Override public FuncResult<String> submit(Task info) { info.prep(); if (this.impl != null) return this.impl.submit(info); FuncResult<String> or = new FuncResult<>(); or.errorTr(172); return or; } @Override public FuncResult<String> startWork(String workid) { if (this.impl != null) return this.impl.startWork(workid); FuncResult<String> or = new FuncResult<>(); or.errorTr(172); return or; } public FuncResult<Task> loadWork(RecordStruct info) { if (this.impl != null) return this.impl.loadWork(info); FuncResult<Task> or = new FuncResult<>(); or.errorTr(172); return or; } public OperationResult failWork(TaskRun task) { task.getTask().withStatus("Failed"); if (this.impl != null) return this.impl.endWork(task); OperationResult or = new OperationResult(); or.errorTr(172); return or; } public OperationResult completeWork(TaskRun task) { // if work is complete, it is the final try task.getTask() .withFinalTry(true) .withStatus("Completed"); if (this.impl != null) return this.impl.endWork(task); OperationResult or = new OperationResult(); or.errorTr(172); return or; } @Override public OperationResult endWork(TaskRun task) { if (this.impl != null) return this.impl.endWork(task); OperationResult or = new OperationResult(); or.errorTr(172); return or; } @Override public OperationResult trackWork(TaskRun task, boolean ended) { if (this.impl != null) return this.impl.trackWork(task, ended); OperationResult or = new OperationResult(); or.errorTr(172); return or; } @Override public void sendAlert(long code, Object... params) { if (this.alerter != null) this.alerter.sendAlert(code, params); } @Override public ListStruct list() { if (this.impl != null) return this.impl.list(); return null; } @Override public RecordStruct status(String taskid, String workid) { if (this.impl != null) return this.impl.status(taskid, workid); return null; } }