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

org.glowroot.instrumentation.elasticsearch.ActionFutureInstrumentation Maven / Gradle / Ivy

/*
 * Copyright 2017-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.glowroot.instrumentation.elasticsearch;

import org.glowroot.instrumentation.api.AsyncQuerySpan;
import org.glowroot.instrumentation.api.Timer;
import org.glowroot.instrumentation.api.checker.Nullable;
import org.glowroot.instrumentation.api.weaving.Advice;
import org.glowroot.instrumentation.api.weaving.Bind;
import org.glowroot.instrumentation.api.weaving.Mixin;

public class ActionFutureInstrumentation {

    // the field and method names are verbose since they will be mixed in to existing classes
    @Mixin("org.elasticsearch.action.ActionFuture")
    public static class ActionFutureImpl implements ActionFutureMixin {

        private transient volatile boolean glowroot$completed;
        private transient volatile @Nullable Throwable glowroot$exception;
        private transient volatile @Nullable AsyncQuerySpan glowroot$asyncQuerySpan;

        @Override
        public void glowroot$setCompleted() {
            glowroot$completed = true;
        }

        @Override
        public boolean glowroot$isCompleted() {
            return glowroot$completed;
        }

        @Override
        public void glowroot$setException(Throwable exception) {
            glowroot$exception = exception;
        }

        @Override
        public @Nullable Throwable glowroot$getException() {
            return glowroot$exception;
        }

        @Override
        public @Nullable AsyncQuerySpan glowroot$getAsyncQuerySpan() {
            return glowroot$asyncQuerySpan;
        }

        @Override
        public void glowroot$setAsyncQuerySpan(@Nullable AsyncQuerySpan asyncQuerySpan) {
            glowroot$asyncQuerySpan = asyncQuerySpan;
        }
    }

    // the method names are verbose since they will be mixed in to existing classes
    public interface ActionFutureMixin {

        void glowroot$setCompleted();

        boolean glowroot$isCompleted();

        void glowroot$setException(Throwable t);

        @Nullable
        Throwable glowroot$getException();

        @Nullable
        AsyncQuerySpan glowroot$getAsyncQuerySpan();

        void glowroot$setAsyncQuerySpan(@Nullable AsyncQuerySpan asyncQuerySpan);
    }

    @Advice.Pointcut(className = "java.util.concurrent.Future",
                     subTypeRestriction = "org.elasticsearch.action.ActionFuture",
                     methodName = "get",
                     methodParameterTypes = {".."},
                     suppressionKey = "wait-on-future")
    public static class FutureGetAdvice {

        @Advice.OnMethodBefore
        public static @Nullable Timer onBefore(@Bind.This ActionFutureMixin actionFuture) {

            AsyncQuerySpan asyncQuerySpan = actionFuture.glowroot$getAsyncQuerySpan();
            if (asyncQuerySpan == null) {
                return null;
            } else {
                return asyncQuerySpan.extendSyncTimer();
            }
        }

        @Advice.OnMethodAfter
        public static void onAfter(@Bind.Enter @Nullable Timer timer) {

            if (timer != null) {
                timer.stop();
            }
        }
    }

    @Advice.Pointcut(className = "org.elasticsearch.common.util.concurrent.BaseFuture",
                     subTypeRestriction = "org.elasticsearch.action.ActionFuture",
                     methodName = "setException",
                     methodParameterTypes = {"java.lang.Throwable"})
    public static class FutureSetExceptionAdvice {

        // using @Advice.OnMethodBefore instead of @Advice.OnMethodExit to ensure that the async
        // span
        // is ended prior to an overall transaction that may be waiting on this future has a chance
        // to end
        @Advice.OnMethodBefore
        public static void onBefore(
                @Bind.This ActionFutureMixin actionFuture,
                @Bind.Argument(0) @Nullable Throwable t) {

            if (t == null) {
                return;
            }
            // to prevent race condition, setting completed/exception status before getting async
            // query entry, and the converse is done when setting async query entry
            // ok if end() happens to get called twice
            actionFuture.glowroot$setCompleted();
            actionFuture.glowroot$setException(t);
            AsyncQuerySpan asyncQuerySpan = actionFuture.glowroot$getAsyncQuerySpan();
            if (asyncQuerySpan != null) {
                asyncQuerySpan.endWithError(t);
            }
        }
    }

    @Advice.Pointcut(className = "org.elasticsearch.common.util.concurrent.BaseFuture",
                     subTypeRestriction = "org.elasticsearch.action.ActionFuture",
                     methodName = "set",
                     methodParameterTypes = {"java.lang.Object"})
    public static class FutureSetAdvice {

        // using @Advice.OnMethodBefore instead of @Advice.OnMethodExit to ensure that the async
        // span
        // is ended prior to an overall transaction that may be waiting on this future has a chance
        // to end
        @Advice.OnMethodBefore
        public static void onBefore(@Bind.This ActionFutureMixin actionFuture) {

            // to prevent race condition, setting completed status before getting async query entry,
            // and the converse is done when setting async query entry
            // ok if end() happens to get called twice
            actionFuture.glowroot$setCompleted();
            AsyncQuerySpan asyncQuerySpan = actionFuture.glowroot$getAsyncQuerySpan();
            if (asyncQuerySpan != null) {
                asyncQuerySpan.end();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy