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

framework.annotation.Job Maven / Gradle / Ivy

package framework.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;

import app.config.Sys;
import framework.Application;
import framework.Db;
import framework.Lazy;
import framework.Log;
import framework.Reflector;
import framework.Request;
import framework.Session;
import framework.Tool;
import framework.Tuple;

/**
 * Setting for job schedule.
 * 
    *
  • {@code @Job("1H")} run every one hour(usable unit is D, H, M, S)
  • *
  • {@code @Job("2")} run on every month 2nd
  • *
  • {@code @Job("12:00")} run 12:00 on every day
  • *
  • {@code @Job(":01")} run 1 minute on every hour
  • *
  • {@code @Job("12:34:56")} run 12:34:56 on every day
  • *
  • {@code @Job("Fri 12:34")} run 12:34 on every Friday
  • *
  • {@code @Job("3 10:00")} run 10:00 on every month 3rd
  • *
  • {@code @Job("2/3 10:00")} run 10:00 on February 3rd
  • *
  • {@code @Job("12:00, 13:00")} run 12:00 and 13:00 on every day
  • *
  • {@code @Job("job.daily")} run at config.txt setting value
  • *
*/ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface Job { /** * @return schedule */ String value(); /** * @return Priority */ int priority() default 0; /** * Job on application start */ static String OnApplicationStart = "OnApplicationStart"; /** * Job on application end */ static String OnApplicationEnd = "OnApplicationEnd"; /** * Job on logged in */ static String OnLoggedIn = "OnLoggedIn"; /** * Job on logged out */ static String OnLoggedOut = "OnLoggedOut"; /** * Job on request */ static String OnRequest = "OnRequest"; /** * Events */ static List events = Tool.list(OnApplicationStart, OnApplicationEnd, OnLoggedIn, OnLoggedOut, OnRequest); /** * Job scheduler */ public static class Scheduler { /** * Scheduler */ static final AtomicReference scheduler = new AtomicReference<>(); /** * Event map */ public static final Map> eventMap = new HashMap<>(); /** * setup * * @param classes target class */ public static void setup(Class... classes) { ZonedDateTime now = ZonedDateTime.now(); for (Class c : classes) { Stream.of(c.getDeclaredMethods()).map(method -> Tuple.of(method, method.getAnnotation(Job.class))).filter(pair -> pair.r != null) .forEach(pair -> { Method method = pair.l; String value = pair.r.value(); if(events.contains(value)) { eventMap.computeIfAbsent(value, k -> Tool.list()).add(method); return; } Stream.of(value.split("\\s*,\\s*")).filter(Tool.notEmpty) .map( j -> j.startsWith("job.") ? Config.Injector.getSource(Sys.class, Session.currentLocale()).getProperty(j, "") : j) .filter(Tool.notEmpty).forEach(text -> { long first = Tool.nextMillis(text, now); String name = c.getName() + '.' + method.getName(); if(first < 0) { Log.info(name + " : job is not scheduled"); return; } if (scheduler.get() == null) { int n = Sys.job_threads; scheduler.set(Executors.newScheduledThreadPool(n)); Log.info(n + " job threads created"); } ZonedDateTime firstStart = now.plus(first, ChronoUnit.MILLIS); Log.info(name + " : job next start at " + firstStart); scheduler.get().schedule(new Runnable() { ZonedDateTime start = firstStart; @Override public void run() { Log.info(name + " : job start - " + start); try (Lazy db = new Lazy<>(Db::connect)) { method.setAccessible(true); method.invoke(Modifier.isStatic(method.getModifiers()) ? null : Reflector.instance(c), Stream.of(method.getParameters()).map(p -> { Class type = p.getType(); if (type == ZonedDateTime.class) { return start; } if (type == LocalDateTime.class) { return start.toLocalDateTime(); } if (type == OffsetDateTime.class) { return start.toOffsetDateTime(); } if (type == Date.class) { return Date.from(start.toInstant()); } if (Application.class.isAssignableFrom(type)) { return Application.current().orElse(null); } if (Session.class.isAssignableFrom(type)) { return Session.current().orElse(null); } if (Request.class.isAssignableFrom(type)) { return Request.current().orElse(null); } if (Db.class.isAssignableFrom(type)) { return db.get(); } if (Optional.class.isAssignableFrom(type)) { return Optional.empty(); } if (boolean.class.isAssignableFrom(type)) { return false; } if (byte.class.isAssignableFrom(type)) { return (byte)0; } if (short.class.isAssignableFrom(type)) { return (short)0; } if (int.class.isAssignableFrom(type)) { return 0; } if (long.class.isAssignableFrom(type)) { return 0L; } if (float.class.isAssignableFrom(type)) { return .0F; } if (double.class.isAssignableFrom(type)) { return .0; } return null; }).toArray()); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Log.warning(e, () -> name + " : job error"); } ZonedDateTime end = ZonedDateTime.now(); Log.info(name + " : job end - " + end + " (" + Duration.between(start, end) + ")"); long next = Tool.nextMillis(text, end); if (next < 1000) { next = Tool.nextMillis(text, end.plusSeconds(1)); } start = end.plus(next, ChronoUnit.MILLIS); Log.info(name + " : job next start at " + start); scheduler.get().schedule(this, next, TimeUnit.MILLISECONDS); } }, first, TimeUnit.MILLISECONDS); }); }); } } /** * shutdown */ public static void shutdown() { Tool.of(scheduler.get()).ifPresent(i -> { i.shutdown(); try { if(!i.awaitTermination(1, TimeUnit.SECONDS)) { i.shutdownNow(); } } catch (InterruptedException e) { i.shutdownNow(); } }); } /** * @param event Event * @return first non-null result */ public static Object trigger(String event) { Object[] result = {null}; try (Lazy db = new Lazy<>(Db::connect)) { eventMap.getOrDefault(event, Collections.emptyList()).stream()// .sorted(Comparator.comparingInt(m -> m.getAnnotation(Job.class).priority()).reversed())// .forEach(method -> { Object r = Reflector.invoke(method, Stream.of(method.getParameters()).map(Parameter::getType).map(type -> { if (Application.class.isAssignableFrom(type)) { return Application.current().orElse(null); } if (Session.class.isAssignableFrom(type)) { return Session.current().orElse(null); } if (Request.class.isAssignableFrom(type)) { return Request.current().orElse(null); } if (Db.class.isAssignableFrom(type)) { return db.get(); } if (Optional.class.isAssignableFrom(type)) { return Optional.empty(); } if (boolean.class.isAssignableFrom(type)) { return false; } if (byte.class.isAssignableFrom(type)) { return (byte)0; } if (short.class.isAssignableFrom(type)) { return (short)0; } if (int.class.isAssignableFrom(type)) { return 0; } if (long.class.isAssignableFrom(type)) { return 0L; } if (float.class.isAssignableFrom(type)) { return .0F; } if (double.class.isAssignableFrom(type)) { return .0; } return null; }).toArray()); if(result[0] == null && r != null) { result[0] = r; } }); } return result[0]; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy