All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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