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

io.vertx.up.atom.worker.Mission Maven / Gradle / Ivy

The newest version!
package io.vertx.up.atom.worker;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ClassDeserializer;
import com.fasterxml.jackson.databind.ClassSerializer;
import com.fasterxml.jackson.databind.JsonObjectDeserializer;
import com.fasterxml.jackson.databind.JsonObjectSerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.aeon.experiment.specification.sch.KTimer;
import io.horizon.atom.app.KApp;
import io.horizon.eon.VMessage;
import io.horizon.eon.VValue;
import io.horizon.eon.em.scheduler.JobStatus;
import io.horizon.eon.em.scheduler.JobType;
import io.horizon.uca.log.Annal;
import io.vertx.core.json.JsonObject;
import io.vertx.up.annotations.Off;
import io.vertx.up.annotations.On;
import io.vertx.up.exception.web._409JobFormulaErrorException;
import io.vertx.up.exception.web._501JobOnMissingException;
import io.vertx.up.fn.Fn;
import io.vertx.up.uca.log.DevEnv;
import io.vertx.up.util.Ut;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Data Object to describe job detail, it stored job definition
 * 1) The definition came from scanned @Job annotation class ( each class should has one Mission )
 * 2) The definition came from JobStore interface ( the job definition may be stored into database or other
 */
public class Mission implements Serializable {
    private static final Annal LOGGER = Annal.get(Mission.class);
    /* Job status, default job is 'starting' */
    private JobStatus status = JobStatus.STARTING;
    /* Job name */
    private String name;
    /* Job type */
    private JobType type;
    /* Job code */
    private String code;
    /* Job description */
    private String comment;
    /* Whether this job is read only */
    private boolean readOnly;
    /*
     * Threshold value for timeout of worker here.
     * This parameter will be bind to current mission for set worker timeout when long time worker executed.
     * It means that the following code will be executed:
     *
     * final WorkerExecutor executor = this.vertx.createSharedWorkerExecutor(code, 1, threshold);
     *
     * For above code, the system will set the timeout parameter of current worker ( Background Job ) and it's
     * not related to scheduling instead.
     *
     * So I moved the code from `KTimer` into `Mission` here.
     *
     * There are two mode focus on this parameter calculation as:
     * 1) From programming part:
     * 2) From configuration part:
     *
     * - The default time unit is TimeUnit.SECONDS
     * - The final result should be `ns`.
     *
     * This field could not be serialized directly, you must call `timeout` to set this value
     * or the worker will use default parameters.
     **/
    @JsonIgnore
    private long threshold = VValue.RANGE;
    /* Job configuration */
    @JsonSerialize(using = JsonObjectSerializer.class)
    @JsonDeserialize(using = JsonObjectDeserializer.class)
    private JsonObject metadata = new JsonObject();
    /* Job additional */
    @JsonSerialize(using = JsonObjectSerializer.class)
    @JsonDeserialize(using = JsonObjectDeserializer.class)
    private JsonObject additional = new JsonObject();

    @JsonSerialize(using = ClassSerializer.class)
    @JsonDeserialize(using = ClassDeserializer.class)
    private Class income;

    private String incomeAddress;
    @JsonSerialize(using = ClassSerializer.class)
    @JsonDeserialize(using = ClassDeserializer.class)
    private Class outcome;

    private String outcomeAddress;

    /* Job reference */
    @JsonIgnore
    private Object proxy;
    /* Job begin method */
    @JsonIgnore
    private Method on;
    /* Job end method */
    @JsonIgnore
    private Method off;
    /*
     * New attribute for
     * Application Scope based on KApp specification here that belong to one KApp information
     * The attribute is as following:
     * {
     *     "name":      "application name",
     *     "ns":        "the default namespace",
     *     "language":  "the default language",
     *     "sigma":     "the uniform sigma identifier"
     * }
     *
     * Be careful that this variable will be used in Zero Extension Framework and it's based on
     * `X_APP` etc here, for programming part it's null before SVN Store connected. In future
     * version all the configuration data will be stored in integration, it means that you can
     * set any information of current Mission reference.
     * */
    @JsonIgnore
    private KApp app;

    @JsonIgnore
    private KTimer timer;

    public JobStatus getStatus() {
        return this.status;
    }

    public void setStatus(final JobStatus status) {
        this.status = status;
    }

    public boolean isReadOnly() {
        return this.readOnly;
    }

    public void setReadOnly(final boolean readOnly) {
        this.readOnly = readOnly;
    }

    public String getName() {
        return this.name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public JobType getType() {
        return this.type;
    }

    public void setType(final JobType type) {
        this.type = type;
    }

    public String getCode() {
        return this.code;
    }

    public void setCode(final String code) {
        this.code = code;
    }

    public String getComment() {
        return this.comment;
    }

    public void setComment(final String comment) {
        this.comment = comment;
    }

    public JsonObject getMetadata() {
        return this.metadata;
    }

    public void setMetadata(final JsonObject metadata) {
        this.metadata = metadata;
    }

    public JsonObject getAdditional() {
        return this.additional;
    }

    public void setAdditional(final JsonObject additional) {
        this.additional = additional;
    }

    public Object getProxy() {
        return this.proxy;
    }

    public void setProxy(final Object proxy) {
        this.proxy = proxy;
    }

    public Method getOn() {
        return this.on;
    }

    public void setOn(final Method on) {
        this.on = on;
    }

    public Method getOff() {
        return this.off;
    }

    public void setOff(final Method off) {
        this.off = off;
    }

    public Class getIncome() {
        return this.income;
    }

    public void setIncome(final Class income) {
        this.income = income;
    }

    public String getIncomeAddress() {
        return this.incomeAddress;
    }

    public void setIncomeAddress(final String incomeAddress) {
        this.incomeAddress = incomeAddress;
    }

    public Class getOutcome() {
        return this.outcome;
    }

    public void setOutcome(final Class outcome) {
        this.outcome = outcome;
    }

    public String getOutcomeAddress() {
        return this.outcomeAddress;
    }

    public void setOutcomeAddress(final String outcomeAddress) {
        this.outcomeAddress = outcomeAddress;
    }

    public Mission connect(final Class clazz) {
        /*
         * Here the system should connect clazz to set:
         * 1. proxy
         *    - on
         *    - off
         * 2. in/out
         *    - income
         *    - incomeAddress
         *    - outcome
         *    - outcomeAddress
         */
        final Object proxy = Ut.singleton(clazz);
        if (Objects.nonNull(proxy)) {
            /*
             * 1. proxy of class has bee initialized successfully
             * Care: The field of other instances will be bind in future after mission
             */
            this.proxy = proxy;
            /*
             * 2. @On
             *  It's required in clazz definition or here should throw exception or errors
             */
            this.on = Arrays.stream(clazz.getDeclaredMethods())
                .filter(method -> method.isAnnotationPresent(On.class))
                .findFirst().orElse(null);
            Fn.out(null == this.on, _501JobOnMissingException.class,
                this.getClass(), clazz.getName());
            /*
             * Income / IncomeAddress
             */
            final Annotation on = this.on.getAnnotation(On.class);
            this.incomeAddress = this.invoke(on, "address", this::getIncomeAddress);
            this.income = this.invoke(on, "income", this::getIncome);
            if (Ut.isNil(this.incomeAddress)) {
                this.incomeAddress = null;
            }

            /*
             * 3. @Off
             * It's optional in clazz definition
             */
            this.off = Arrays.stream(clazz.getDeclaredMethods())
                .filter(method -> method.isAnnotationPresent(Off.class))
                .findFirst().orElse(null);
            if (Objects.nonNull(this.off)) {
                /*
                 * Outcome / OutcomeAddress
                 */
                final Annotation out = this.off.getAnnotation(Off.class);
                this.outcomeAddress = this.invoke(out, "address", this::getOutcomeAddress);
                this.outcome = this.invoke(out, "outcome", this::getOutcome);
                if (Ut.isNil(this.outcomeAddress)) {
                    this.outcomeAddress = null;
                }
            }
            if (DevEnv.devJobBoot()) {
                LOGGER.info(VMessage.Job.MISSION.JOB_OFF, this.getCode());
            }
        }
        return this;
    }

    private  T invoke(final Annotation annotation, final String annotationMethod,
                         final Supplier supplier) {
        /*
         * Stored in database / or @Job -> config file
         */
        T reference = supplier.get();
        if (Objects.isNull(reference)) {
            /*
             * Annotation extraction
             */
            reference = Ut.invoke(annotation, annotationMethod);
        }
        return reference;
    }

    public Mission timeout(final Integer input, final TimeUnit unit) {
        if (Objects.isNull(input)) {
            this.threshold = VValue.RANGE;
            return this;
        } else {
            this.threshold = unit.toNanos(input);
            return this;
        }
    }

    public long timeout() {
        if (VValue.RANGE == this.threshold) {
            // The default timeout is 15 min
            return TimeUnit.MINUTES.toNanos(15);
        } else {
            return this.threshold;
        }
    }

    // ========================== KApp Information =======================
    // The bind method on KApp
    public Mission app(final KApp app) {
        this.app = app;
        return this;
    }

    public KApp app() {
        return this.app;
    }

    public Mission timer(final KTimer timer) {
        this.timer = timer;
        return this;
    }

    public KTimer timer() {
        return this.timer;
    }

    // ========================== Ensure the correct configuration =======================
    public void detectPre(final String formula) {
        if (JobType.FORMULA == this.type) {
            Fn.outWeb(Ut.isNil(formula), _409JobFormulaErrorException.class, this.getClass(), formula);
        }
    }

    @Override
    public String toString() {
        return "Mission{" +
            "status=" + this.status +
            ", name='" + this.name + '\'' +
            ", readOnly='" + this.readOnly + '\'' +
            ", type=" + this.type +
            ", code='" + this.code + '\'' +
            ", comment='" + this.comment + '\'' +
            ", metadata=" + this.metadata +
            ", additional=" + this.additional +
            ", income=" + this.income +
            ", incomeAddress='" + this.incomeAddress + '\'' +
            ", outcome=" + this.outcome +
            ", outcomeAddress='" + this.outcomeAddress + '\'' +
            ", proxy=" + this.proxy +
            ", on=" + this.on +
            ", off=" + this.off +
            ", app=" + this.app +
            ", timer=" + this.timer +
            '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy