/*
 * Copyright 2017-2020 original 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
 *
 * https://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 io.micronaut.scheduling.instrument;

import io.micronaut.core.annotation.Experimental;
import io.micronaut.core.util.CollectionUtils;

import edu.umd.cs.findbugs.annotations.NonNull;

import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;

import static io.micronaut.core.util.ArgumentUtils.requireNonNull;

An interface for invocation instrumentation.
Author:Denis Stepanov, graemerocher
Since:1.3
/** * An interface for invocation instrumentation. * * @author Denis Stepanov * @author graemerocher * @since 1.3 */
@Experimental public interface InvocationInstrumenter {
Noop implementation if InvocationInstrumenter.
/** * Noop implementation if {@link InvocationInstrumenter}. */
InvocationInstrumenter NOOP = Instrumentation::noop;
Returns:a one-time Instrumentation instance which to be used in a try-with-resources to do the instrumentation. To force cleanup invoke Instrumentation.forceCleanup() on the retuned instance.
Since:2.0
/** * @return a one-time {@link Instrumentation} instance which to be used in a try-with-resources to do the * instrumentation. To force cleanup invoke {@link Instrumentation#forceCleanup()} on the retuned instance. * @since 2.0 */
@NonNull Instrumentation newInstrumentation();
Combines multiple instrumenters into one.
Params:
  • invocationInstrumenters – instrumenters to combine
Returns:new instrumenter
/** * Combines multiple instrumenters into one. * * @param invocationInstrumenters instrumenters to combine * @return new instrumenter */
static @NonNull InvocationInstrumenter combine(Collection<InvocationInstrumenter> invocationInstrumenters) { if (CollectionUtils.isEmpty(invocationInstrumenters)) { return NOOP; } if (invocationInstrumenters.size() == 1) { return invocationInstrumenters.iterator().next(); } return new MultipleInvocationInstrumenter(invocationInstrumenters); }
Wraps Runnable with instrumentation invocations.
Params:
  • runnable – Runnable to be wrapped
  • invocationInstrumenters – instrumenters to be used
Returns:wrapper
/** * Wraps {@link Runnable} with instrumentation invocations. * * @param runnable {@link Runnable} to be wrapped * @param invocationInstrumenters instrumenters to be used * @return wrapper */
static @NonNull Runnable instrument(@NonNull Runnable runnable, Collection<InvocationInstrumenter> invocationInstrumenters) { if (CollectionUtils.isEmpty(invocationInstrumenters)) { return runnable; } return instrument(runnable, combine(invocationInstrumenters)); }
Wraps Callable with instrumentation invocations.
Params:
  • callable – Callable to be wrapped
  • invocationInstrumenters – instrumenters to be used
Type parameters:
  • <V> – callable generic param
Returns:wrapper
/** * Wraps {@link Callable} with instrumentation invocations. * * @param callable {@link Callable} to be wrapped * @param invocationInstrumenters instrumenters to be used * @param <V> callable generic param * @return wrapper */
static @NonNull <V> Callable<V> instrument(@NonNull Callable<V> callable, Collection<InvocationInstrumenter> invocationInstrumenters) { if (CollectionUtils.isEmpty(invocationInstrumenters)) { return callable; } return instrument(callable, combine(invocationInstrumenters)); }
Wraps Runnable with instrumentation invocations.
Params:
  • runnable – Runnable to be wrapped
  • invocationInstrumenter – instrumenter to be used
Returns:wrapper
/** * Wraps {@link Runnable} with instrumentation invocations. * * @param runnable {@link Runnable} to be wrapped * @param invocationInstrumenter instrumenter to be used * @return wrapper */
static @NonNull Runnable instrument(@NonNull Runnable runnable, InvocationInstrumenter invocationInstrumenter) { if (runnable instanceof InvocationInstrumenterWrappedRunnable) { return runnable; } return new InvocationInstrumenterWrappedRunnable(invocationInstrumenter, runnable); }
Wraps Callable with instrumentation invocations.
Params:
  • callable – Callable to be wrapped
  • invocationInstrumenter – instrumenter to be used
Type parameters:
  • <V> – callable generic param
Returns:wrapper
/** * Wraps {@link Callable} with instrumentation invocations. * * @param callable {@link Callable} to be wrapped * @param invocationInstrumenter instrumenter to be used * @param <V> callable generic param * @return wrapper */
static @NonNull <V> Callable<V> instrument(@NonNull Callable<V> callable, InvocationInstrumenter invocationInstrumenter) { if (callable instanceof InvocationInstrumenterWrappedCallable) { return callable; } return new InvocationInstrumenterWrappedCallable<>(invocationInstrumenter, callable); }
Wraps the executor so that every tasks submitted to it will be executed instrumented with the given invocationInstrumenter. Execution itself will be delegated to the underlying executor, but it has to be considered that all instrumentation will be done with this very same invocationInstrumenter instance. This is especially useful when follow-up actions of a given task need to be registered, where a new instrumenter, thus a new wrapped executor instance belongs to each task.

The returned wrapped executor be of subtype ExecutorService or ScheduledExecutorService if the input executor instance implemented those interfaces.
Params:
  • executor – the executor to wrap
  • invocationInstrumenter – the instrumenter to be used upon task executions with the returned executor
Returns:the wrapped executor
/** * Wraps the {@code executor} so that every tasks submitted to it will be executed instrumented with the given * {@code invocationInstrumenter}. Execution itself will be delegated to the underlying {@code executor}, but it has * to be considered that all instrumentation will be done with this very same {@code invocationInstrumenter} * instance. This is especially useful when follow-up actions of a given task need to be registered, where a new * instrumenter, thus a new wrapped executor instance belongs to each task. * <p/> * The returned wrapped executor be of subtype {@link ExecutorService} or {@link ScheduledExecutorService} if the * input executor instance implemented those interfaces. * * @param executor the executor to wrap * @param invocationInstrumenter the instrumenter to be used upon task executions with the returned executor * @return the wrapped executor */
static Executor instrument(@NonNull Executor executor, @NonNull InvocationInstrumenter invocationInstrumenter) { requireNonNull("executor", executor); requireNonNull("invocationInstrumenter", invocationInstrumenter); if (executor instanceof ScheduledExecutorService) { return new InstrumentedScheduledExecutorService() { @Override public ScheduledExecutorService getTarget() { return (ScheduledExecutorService) executor; } @Override public <T> Callable<T> instrument(Callable<T> callable) { return InvocationInstrumenter.instrument(callable, invocationInstrumenter); } @Override public Runnable instrument(Runnable runnable) { return InvocationInstrumenter.instrument(runnable, invocationInstrumenter); } }; } else if (executor instanceof ExecutorService) { return new InstrumentedExecutorService() { @Override public ExecutorService getTarget() { return (ExecutorService) executor; } @Override public <T> Callable<T> instrument(Callable<T> callable) { return InvocationInstrumenter.instrument(callable, invocationInstrumenter); } @Override public Runnable instrument(Runnable runnable) { return InvocationInstrumenter.instrument(runnable, invocationInstrumenter); } }; } else { return new InstrumentedExecutor() { @Override public Executor getTarget() { return executor; } @Override public Runnable instrument(Runnable runnable) { return InvocationInstrumenter.instrument(runnable, invocationInstrumenter); } }; } } }