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

com.jcabi.aspects.aj.MethodScheduler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2012-2024, jcabi.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met: 1) Redistributions of source code must retain the above
 * copyright notice, this list of conditions and the following
 * disclaimer. 2) Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution. 3) Neither the name of the jcabi.com nor
 * the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jcabi.aspects.aj;

import com.jcabi.aspects.ScheduleWithFixedDelay;
import com.jcabi.log.Logger;
import com.jcabi.log.VerboseRunnable;
import com.jcabi.log.VerboseThreads;
import java.io.Closeable;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * Schedules methods.
 *
 * @since 0.7.16
 */
@Aspect
@SuppressWarnings("PMD.DoNotUseThreads")
public final class MethodScheduler {

    /**
     * Objects and their running services.
     */
    private final transient
        ConcurrentMap services;

    /**
     * Ctor.
     */
    public MethodScheduler() {
        this.services =
            new ConcurrentHashMap<>(0);
    }

    /**
     * Instantiate a new routine task.
     *
     * 

Try NOT to change the signature of this method, in order to keep * it backward compatible. * * @param point Joint point * @checkstyle LineLength (2 lines) */ @After("initialization((@com.jcabi.aspects.ScheduleWithFixedDelay *).new(..))") public void instantiate(final JoinPoint point) { final Object object = point.getTarget(); if (this.services.containsKey(object)) { throw new IllegalStateException( Logger.format( "%[type]s was already scheduled for execution", object ) ); } final Runnable runnable; if (object instanceof Runnable) { runnable = new VerboseRunnable((Runnable) object, true); } else if (object instanceof Callable) { runnable = new VerboseRunnable((Callable) object, true); } else { throw new IllegalStateException( Logger.format( "%[type]s doesn't implement Runnable or Callable", object ) ); } this.services.put( object, new MethodScheduler.Service( runnable, object, object.getClass().getAnnotation(ScheduleWithFixedDelay.class) ) ); } /** * Stop/close a routine task. * *

Try NOT to change the signature of this method, in order to keep * it backward compatible. * * @param point Joint point * @checkstyle LineLength (2 lines) */ @Before("execution(* (@com.jcabi.aspects.ScheduleWithFixedDelay *).close())") public void close(final JoinPoint point) { final Object object = point.getTarget(); this.services.get(object).close(); this.services.remove(object); } /** * Running service. * @since 0.0.0 */ private static final class Service implements Closeable { /** * Running scheduled service. */ private final transient ScheduledExecutorService executor; /** * The object. */ private final transient Object object; /** * Execution counter. */ private final transient AtomicLong counter; /** * When started. */ private final transient long start; /** * How long to wait for the task to finish. */ private final transient long await; /** * Shutdown attempts count. */ private final transient long attempts; /** * Should more information be logged? */ private final transient boolean verbose; /** * Public ctor. * @param runnable The runnable to schedule * @param obj Object * @param annt Annotation */ @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors") Service(final Runnable runnable, final Object obj, final ScheduleWithFixedDelay annt) { this.start = System.currentTimeMillis(); this.counter = new AtomicLong(); this.object = obj; this.executor = Executors.newScheduledThreadPool( annt.threads(), new VerboseThreads(this.object) ); this.verbose = annt.verbose(); this.await = annt.awaitUnit().toMillis( (long) annt.await() ); this.attempts = (long) annt.shutdownAttempts(); final Runnable job = () -> { runnable.run(); this.counter.incrementAndGet(); }; for (int thread = 0; thread < annt.threads(); ++thread) { this.executor.scheduleWithFixedDelay( job, (long) annt.delay(), (long) annt.delay(), annt.unit() ); } if (this.verbose) { Logger.info( this.object, "scheduled for execution with %d %s interval", annt.delay(), annt.unit() ); } } @Override public void close() { this.executor.shutdown(); final long begin = System.currentTimeMillis(); try { while (true) { if (this.executor.awaitTermination(1L, TimeUnit.SECONDS)) { break; } final long age = System.currentTimeMillis() - begin; if (age > this.await) { break; } if (this.verbose) { Logger.info( this, "waiting %[ms]s for threads termination", age ); } } for (int attempt = 0; (long) attempt < this.attempts; ++attempt) { this.executor.shutdownNow(); this.executor.awaitTermination(1L, TimeUnit.SECONDS); } if (!this.executor.isTerminated()) { throw new IllegalStateException( Logger.format( "failed to shutdown %[type]s of %[type]s", this.executor, this.object ) ); } } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); throw new IllegalStateException(ex); } if (this.verbose && Logger.isInfoEnabled(this.object)) { Logger.info( this.object, "execution stopped after %[ms]s and %d tick(s)", System.currentTimeMillis() - this.start, this.counter.get() ); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy