
automately.core.services.job.JobUtil Maven / Gradle / Ivy
package automately.core.services.job;
import automately.core.data.Job;
import automately.core.data.User;
import automately.core.data.comparators.JobComparator;
import com.hazelcast.core.ICountDownLatch;
import com.hazelcast.core.ILock;
import com.hazelcast.core.IMap;
import com.hazelcast.core.ISet;
import com.hazelcast.query.*;
import io.jcluster.JCluster;
import io.jcluster.core.Cluster;
import io.jsync.eventbus.EventBus;
import io.jsync.json.JsonObject;
import java.util.Collection;
import java.util.Date;
import static automately.core.data.UserData.getUser;
/**
* The JobUtil is a utility for that can be used
*/
public class JobUtil {
/**
* Begin static code initialization.
*/
static {
if(JCluster.activeInstance() == null){
throw new Error("Could not initialize JobUtil because the JCluster instance does not seem to be active.");
}
}
private static Cluster cluster = JCluster.activeInstance().cluster();
private static IMap jobs = cluster.data().persistentMap("jobs");
private static ISet jobsBeingExecuted = cluster.data().getSet("jobs.executing");
private static IMap jobExecutionNodes = cluster.data().getMap("jobs.executing.nodes");
private static IMap registeredJobServers = cluster.data().getMap("job.server.nodes");
/**
* End static code initialization.
*/
private JobUtil(){}
/**
* isStale is used to check if an Automately Job is stale in the cluster. It will
* return false if the job is not stale. When a job is stale it basically means that
*
* @param job the Job you wish to check
* @return returns true if the job is stale
*/
public static boolean isStale(Job job) {
// TODO optimize this heavily
if (job == null) {
throw new NullPointerException();
}
ICountDownLatch globalJobFinishLatch = cluster.hazelcast().getCountDownLatch(job.token() + "_job_finish_latch");
String status = job.status;
// Return false since the job pretty much has already been handled
if (status.equals("complete") || status.equals("halted") || status.equals("stopped") || status.equals("timeout")) {
return false;
}
final boolean[] isStale = {false};
// Newest method for checking for stale jobs
// TWe are checking to see if there is a lock on the job within the cluster
// If the job is not being executed then there will be no lock so if it's locked
// The job is not stale
ILock executionLock = cluster.hazelcast().getLock("_job_lock_execution_" + job.token());
if(!executionLock.isLocked()){
isStale[0] = true;
}
// If the job is running, queued, processing we should definitely check if it is stale
if (status.equals("running") || status.equals("queued") || status.equals("processing")) {
// This means the job is being executed and a node is handling it.
// So we should check if the node still exists
if(jobsBeingExecuted.contains(job.token()) && jobExecutionNodes.containsKey(job.token())){
String nodeId = jobExecutionNodes.get(job.token());
// This means it was removed the server was removed. So the job is stale
if(!registeredJobServers.containsKey(nodeId)){
isStale[0] = true;
}
} else if (!jobsBeingExecuted.contains(job.token())) {
isStale[0] = true;
}
}
if(isStale[0]){
job.status = "complete";
JsonObject error = new JsonObject();
error.putString("message", "The job has went stale. It is no longer being executed.");
error.putString("code", "Stale Job");
job.results.putObject("error", error);
job.results.putBoolean("success", false);
jobs.set(job.token(), job);
if (globalJobFinishLatch.trySetCount(1)) {
globalJobFinishLatch.countDown();
}
jobsBeingExecuted.remove(job.token());
jobExecutionNodes.remove(job.token());
}
return isStale[0];
}
/**
* getUserJob allows you to retrieve a User's Job from the Cluster via
* it's token.
*
* @param user the User you wish to retrieve the Job from
* @param jobToken the token for the Job you are attempting to find
* @return returns the Job if it was found or null if it wasn't
*/
public static Job getJob(User user, String jobToken) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token())
.and(e.get("token").equal(jobToken));
for (Job job : jobs.values(predicate)) {
if (job.token().equals(jobToken)) {
return job;
}
}
return null;
}
/**
* getUserJobs retrieves a Collection for every single Job belonging
* to the specified User.
*
* @param user the User you wish to retrieve the Job's for
* @return returns a Colllection for the User
*/
public static Collection getJobs(User user) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token());
return jobs.values(predicate);
}
/**
* getUserJobs retrieves a Collection for every single Job belonging
* to the specified User. This returns 10 results by default.
*
* @param user the User you wish to retrieve the Job's for
* @param page the page index starting from 0 you are looking for
* @return returns a Colllection for the User
*/
public static Collection getJobs(User user, int page) {
return getJobs(user, page, 10);
}
/**
* getUserJobs retrieves a Collection for every single Job belonging
* to the specified User.
*
* @param user the User you wish to retrieve the Job's for
* @param page the page index starting from 0 you are looking for
* @param count the number of results to return
* @return returns a Colllection for the User
*/
public static Collection getJobs(User user, int page, int count) {
EntryObject e = new PredicateBuilder().getEntryObject();
com.hazelcast.query.Predicate userJobsPredicate = e.get("userToken").equal(user.token());
if (page < 0) {
page = 0;
}
if (count < 0) {
count = 10;
}
// Max Count is always 100
if (count > 100) count = 100;
// This predicate uses the previous one.. and then sorts the posts by date...
// IMPORTANT apparently can't lambda
PagingPredicate pagingPredicate = new PagingPredicate(userJobsPredicate, new JobComparator(), count);
Collection values = jobs.values(pagingPredicate);
if (count > pagingPredicate.getPage()) {
while (page > pagingPredicate.getPage()) {
pagingPredicate.nextPage();
}
values = jobs.values(pagingPredicate);
}
return values;
}
/**
* getRunningJobs will return a Collection containing all
* of the "normal" running jobs for the user.
*
* @param user the User you wish to retrieve the Collection for
* @return the Collection you are retrieving
*/
public static Collection getRunningJobs(User user) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token())
.and(e.get("service").equal(false))
.and(e.get("lite").equal(false))
.and(e.get("status").equal("running"));
// We only return running service jobs
return jobs.values(predicate);
}
/**
* getRunningJobs will return a Collection containing all
* of the "normal" running jobs for the user.
*
* @param user the User you wish to retrieve the Collection for
* @return the Collection you are retrieving
*/
public static Collection getRunningLiteJobs(User user) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token())
.and(e.get("service").equal(false))
.and(e.get("lite").equal(true))
.and(e.get("status").equal("running"));
// We only return running service jobs
return jobs.values(predicate);
}
/**
* getRunningServices will return a Collection containing all
* of the running service jobs for the user.
*
* @param user the User you wish to retrieve the Collection for
* @return the Collection you are retrieving
*/
public static Collection getRunningServices(User user) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token())
.and(e.get("service").equal(true))
.and(e.get("lite").equal(false))
.and(e.get("status").equal("running"));
return jobs.values(predicate);
}
/**
* getService is used to retrieve a service Job via it's serviceName.
*
* @param user the User you wish to retrieve the Job for
* @param serviceName the serviceName for the Job you wish to retrieve
* @return the Job you wish to retrieve or null if it doesn't exist
*/
public static Job getService(User user, String serviceName) {
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get("userToken").equal(user.token())
.and(e.get("service").equal(true))
.and(e.get("lite").equal(false))
.and(e.get("status").equal("running"))
.and(e.get("serviceName").equal(serviceName));
// We only want to get running services
Collection values = jobs.values(predicate);
if (values.iterator().hasNext()) {
return values.iterator().next();
}
return null;
}
/**
* publishJobEvent is a tool used to publish events on the internal JobServer such as
* halt, error, etc.
*
* @param job the Job you are publishing the event for
* @param event the event you are publishing
*/
public static void publishEvent(Job job, String event) {
String jobEventIdentifier = "job.server." + job.token() + ".events";
EventBus eventBus = cluster.eventBus();
eventBus.publish(jobEventIdentifier, event.trim());
User user = getUser(job.userToken);
if(user != null){
JsonObject allMessage = new JsonObject();
allMessage.putString("job", job.token());
allMessage.putString("event", event);
cluster.eventBus().publish("messageBus_internal_" + user.token() + "_sdk.private." + user.key + ".sdk.job.all.events", allMessage);
}
}
/**
* updateJobStatus allows you to simply update a Job's status from something like
* error to complete.
*
* @param job the Job you are setting the status for
* @param status the status you are setting
*/
public static void updateStatus(Job job, String status) {
status = status.trim().toLowerCase();
cluster.logger().info("Updating Job status for the job " + job.token() + ": " + status);
job.status = status;
job.updated = new Date();
jobs.set(job.token(), job);
publishEvent(job, status);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy