io.helixservice.feature.worker.BlockingWorkerAspect.java Source code

Java tutorial

Introduction

Here is the source code for io.helixservice.feature.worker.BlockingWorkerAspect.java

Source

/*
 * @author Les Novell
 *
 *   All rights reserved. This program and the accompanying materials
 *   are made available under the terms of the Eclipse Public License v1.0
 *   and Apache License v2.0 which accompanies this distribution.
 *
 *      The Apache License v2.0 is available at
 *      http://www.opensource.org/licenses/apache2.0.php
 *
 */

package io.helixservice.feature.worker;

import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;
import io.helixservice.core.container.Container;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.Consumer;

import static io.vertx.ext.sync.Sync.awaitEvent;

/**
 * AspectJ aspect for implementing Blocking Workers feature
 */
@Aspect
@DeclarePrecedence(value = "io.helixservice.feature.worker.BlockingWorkerAspect,*")
public class BlockingWorkerAspect {
    private static Logger LOG = LoggerFactory.getLogger(BlockingWorkerAspect.class);

    private static final String VERT_X_WORKER_THREAD = "vert.x-worker-thread";

    private Vertx vertx;

    public BlockingWorkerAspect() {
    }

    /**
     * Capture Helix server reference on BlockingWorkerFeature creation
     *
     * @param container Helix server
     */
    @Before(value = "execution(public void io.helixservice.feature.worker.BlockingWorkerFeature.start(io.helixservice.core.container.Container)) "
            + "&& args(container)")
    public void beforeStartupFeature(Container container) {
        this.vertx = container.getVertx().get();
    }

    /**
     * Adds code around all methods annotated with @BlockingWorker.
     * If the current thread is not a already worker thread, then
     * the method body is run on a worker thread.
     */
    @Suspendable
    @Around(value = "(execution(public * *(..)) && @annotation(blockingWorker)) || "
            + "(execution(public * *(..)) && within(@io.helixservice.feature.worker.BlockingWorker *) && @annotation(blockingWorker))")
    public Object around(ProceedingJoinPoint pjp, BlockingWorker blockingWorker)
            throws Throwable, SuspendExecution {
        Object result;

        if (onWorkerThread()) {
            result = pjp.proceed();
        } else {
            result = invokeOnWorkerThread(pjp);
        }

        return result;
    }

    public static boolean onWorkerThread() {
        return Thread.currentThread().getName().contains(VERT_X_WORKER_THREAD);
    }

    @Suspendable
    private Object invokeOnWorkerThread(ProceedingJoinPoint pjp) throws Throwable, SuspendExecution {
        AsyncResult<Object> ret = awaitEvent(new Consumer<Handler<AsyncResult<Object>>>() {
            @Override
            @Suspendable
            public void accept(Handler<AsyncResult<Object>> awaitHandler) {
                // Vert.x executeBlocking will run our handler on a Worker Thread
                vertx.executeBlocking(new Handler<Future<Object>>() {
                    @Override
                    @Suspendable
                    public void handle(Future<Object> future) {
                        try {
                            assertRunningOnVertxWorkerThread(pjp);

                            if (LOG.isInfoEnabled()) {
                                LOG.info("Started Blocking Worker on " + Thread.currentThread().getName());
                            }

                            // Run annotated method code
                            Object result = pjp.proceed();

                            // Return result as a completable future
                            future.complete(result);
                        } catch (Throwable t) {
                            // If there was an exception, pass it back on the completable future
                            future.fail(t);
                        } finally {
                            if (LOG.isInfoEnabled()) {
                                LOG.info("Completed Blocking Worker on " + Thread.currentThread().getName());
                            }
                        }
                    }
                }, false, awaitHandler);
            }
        });

        if (ret.failed()) {
            throw ret.cause();
        }

        return ret.result();
    }

    private void assertRunningOnVertxWorkerThread(ProceedingJoinPoint pjp) {
        if (!onWorkerThread()) {
            /**
             * This should never happen, it's a sanity check
             */
            Signature signature = pjp.getSignature();
            throw new IllegalStateException("Expected " + signature.getDeclaringTypeName() + "::"
                    + signature.getName() + " to run on a Vert.x worker thread");
        }
    }
}