org.structr.agent.Agent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of structr-core Show documentation
Show all versions of structr-core Show documentation
Structr is an open source framework based on the popular Neo4j graph database.
The newest version!
/**
* Copyright (C) 2010-2016 Structr GmbH
*
* This file is part of Structr .
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Structr. If not, see .
*/
package org.structr.agent;
//~--- JDK imports ------------------------------------------------------------
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.structr.core.Services;
import org.structr.core.app.StructrApp;
import org.structr.core.graph.NodeInterface;
import org.structr.core.graph.Tx;
import org.structr.util.LogMessageSupplier;
//~--- classes ----------------------------------------------------------------
/**
* Abstract base class for all agents.
*
*
*/
public abstract class Agent extends Thread implements StatusInfo {
public static final String AVERAGE_EXECUTION_TIME = "average_execution_time";
public static final String EXECUTION_STATUS = "execution_status";
public static final String MAX_QUEUE_SIZE = "max_queue_size";
private static final Logger logger = Logger.getLogger(Agent.class.getName());
//~--- fields ---------------------------------------------------------
private final AtomicBoolean acceptingTasks = new AtomicBoolean(true);
private AgentService agentService = null;
private long averageExecutionTime = 0;
private Task currentTask = null;
private long lastStartTime = 0;
private int maxAgents = 4;
private int maxQueueSize = 10;
private final AtomicBoolean suspended = new AtomicBoolean(false);
private final Queue> taskQueue = new ConcurrentLinkedQueue<>();
//~--- methods --------------------------------------------------------
@Override
public final void run() {
agentService.notifyAgentStart(this);
do {
while (suspended.get()) {
Thread.yield();
}
synchronized (taskQueue) {
currentTask = taskQueue.poll();
}
if (currentTask != null) {
lastStartTime = System.nanoTime();
ReturnValue ret = null;
// only execute process if Service layer is ready
// (and not shutting down right now)
if (Services.getInstance().isInitialized()) {
if (createEnclosingTransaction()) {
try (final Tx tx = StructrApp.getInstance().tx()) {
ret = processTask(currentTask);
tx.success();
} catch (Throwable t) {
// someone killed us or the task processing failed..
// Log this!!
logger.log(Level.SEVERE, t, LogMessageSupplier.create("Processing task {0} failed. Maybe someone killed us?", currentTask.getType()));
}
} else {
try {
ret = processTask(currentTask);
} catch (Throwable t) {
// someone killed us or the task processing failed..
// Log this!!
logger.log(Level.SEVERE, t, LogMessageSupplier.create("Processing task {0} failed. Maybe someone killed us?", currentTask.getType()));
}
}
}
if (ret != null) {
// handle return value
switch (ret) {
case Success :
case Abort :
// task finished, nothing to do in these cases
break;
case Retry :
// TODO: schedule task for re-execution
break;
}
}
long endTime = System.nanoTime();
// calc. average execution time
averageExecutionTime += endTime;
averageExecutionTime /= 2;
} else {
// queue is empty, quit.
acceptingTasks.set(false);
}
} while (acceptingTasks.get());
// call beforeShutdown to allow agents to clean up
beforeShutdown();
agentService.notifyAgentStop(this);
}
public final boolean assignTask(final Task task) {
// TODO: do type check here
if (canHandleMore() && acceptingTasks.get()) {
synchronized (taskQueue) {
taskQueue.add(task);
}
return (true);
}
return (false);
}
public final void killAgent() {
// stop accepting tasks
acceptingTasks.set(false);
// clear queue
taskQueue.clear();
// interrupt running process..
// not sure if this works... see Thread.interrupt()'s description!
// may not work if the processTask method itself catches the interrupt..
this.interrupt();
}
public final void suspendAgent() {
acceptingTasks.set(false);
suspended.set(true);
}
public final void resumeAgent() {
acceptingTasks.set(true);
suspended.set(false);
}
protected void beforeShutdown() {
// override me
}
//
//
private boolean canHandleMore() {
// return (taskQueue.isEmpty());
int size = 0;
synchronized (taskQueue) {
// FIXME: size may not be a constant time operation! slow?
size = taskQueue.size();
}
// queue is empty, assume new agent
if (size == 0) {
return (true);
}
long actualExecutionTime = System.nanoTime() - lastStartTime;
// calculate thresholds for queue size adaption
long upperThreshold = averageExecutionTime + (averageExecutionTime / 2);
long lowerThreshold = averageExecutionTime - (averageExecutionTime / 2);
if ((actualExecutionTime > upperThreshold) && (maxQueueSize > 2)) {
// FIXME
maxQueueSize -= 2;
// do not take the next task
return (false);
} else if ((actualExecutionTime < lowerThreshold) && (maxQueueSize < 200)) {
// FIXME
maxQueueSize += 2;
// can take the next task
return (true);
}
return (size < maxQueueSize);
}
//
/**
* This method will be called by the AgentService
* @param task
*/
public abstract ReturnValue processTask(final Task task) throws Throwable;
public boolean createEnclosingTransaction() {
return true;
}
public final Task getCurrentTask() {
return (currentTask);
}
public final List> getTaskQueue() {
List> ret = new LinkedList>();
ret.addAll(taskQueue);
return (ret);
}
public final int getMaxQueueSize() {
return (maxQueueSize);
}
public final long getAverageExecutionTime() {
return (averageExecutionTime);
}
public int getMaxAgents() {
return (maxAgents);
}
// ----- interface StatusInfo -----
@Override
public Object getStatusProperty(String key) {
if (key.equals(AVERAGE_EXECUTION_TIME)) {
return (getAverageExecutionTime());
} else if (key.equals(MAX_QUEUE_SIZE)) {
return (getMaxQueueSize());
} else if (key.equals(EXECUTION_STATUS)) {
// TODO.
}
return (null);
}
//
//
protected AgentService getBlackboardService() {
return (agentService);
}
//
public abstract Class getSupportedTaskType();
public final boolean isSuspended() {
return (suspended.get());
}
public final boolean isAcceptingTasks() {
return (acceptingTasks.get());
}
//~--- set methods ----------------------------------------------------
//
public final void setAgentService(AgentService service) {
// NOTE: this is important! We do not want running tasks to die when the
// server is going down!
this.setDaemon(false);
this.agentService = service;
// initialize lastStartTime
lastStartTime = System.nanoTime();
}
//
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy