<commit_before>/*
 * Copyright (c) 2010-2016. Axon Framework
 *
 * 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.axonframework.commandhandling.gateway;

import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.CommandCallback;
import org.axonframework.commandhandling.CommandExecutionException;
import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.commandhandling.callbacks.FutureCallback;
import org.axonframework.common.Assert;
import org.axonframework.common.CollectionUtils;
import org.axonframework.common.ReflectionUtils;
import org.axonframework.messaging.MessageDispatchInterceptor;
import org.axonframework.messaging.annotation.MetaData;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static java.util.Arrays.asList;
import static org.axonframework.commandhandling.GenericCommandMessage.asCommandMessage;

/**
 * Factory that creates Gateway implementations from custom interface definitions. The behavior of the method is
 * defined by the parameters, declared exceptions and return type of the method.
 * <p/>
 * <em>Supported parameter types:</em><ul>
 * <li>The first parameter of the method is considered the payload of the message. If the first parameter is a Message
 * itself, a new message is created using the payload and metadata of the message passed as parameter.</li>
 * <li>Parameters that are annotated with {@link MetaData @MetaData} are will cause the parameter values to be added as
 * meta data values to the outgoing message.</li>
 * <li>If the last two parameters are of type {@link Long long} and {@link TimeUnit}, they are considered to represent
 * the timeout for the command. The method will block for as long as the command requires to execute, or until the
 * timeout expires.</li>
 * </ul>
 * <p/>
 * <em>Effect of return values</em><ul>
 * <li>{@code void} return types are always allowed. Unless another parameter makes the method blocking, void
 * methods are non-blocking by default.</li>
 * <li>Declaring a {@link Future} return type will always result in a non-blocking operation. A future is returned
 * that allows you to retrieve the execution's result at your own convenience. Note that declared exceptions and
 * timeouts are ignored.</li>
 * <li>Any other return type will cause the dispatch to block (optionally with timeout) until a result is
 * available</li>
 * </ul>
 * <p/>
 * <em>Effect of declared exceptions</em> <ul>
 * <li>Any checked exception declared on the method will cause it to block (optionally with timeout). If the command
 * results in a declared checked exception, that exception is thrown from the method.</li>
 * <li>Declaring a {@link TimeoutException} will throw that exception when a configured timeout expires. If no such
 * exception is declared, but a timeout is configured, the method will return {@code null}.</li>
 * <li>Declaring an {@link InterruptedException} will throw that exception when a thread blocked while waiting for a
 * response is interrupted. Not declaring the exception will have the method return {@code null} when a blocked
 * thread is interrupted. Note that when no InterruptedException is declared, the interrupt flag is set back on the
 * interrupted thread</li>
 * </ul>
 * <p/>
 * <em>Effect of unchecked exceptions</em> <ul>
 * <li>Any unchecked exception thrown during command handling will cause it to block. If the method is blocking (see
 * below) the
 * unchecked exception will be thrown from the method</li>
 * </ul>
 * <p/>
 * Finally, the {@link Timeout @Timeout} annotation can be used to define a timeout on a method. This will always cause
 * a method invocation to block until a response is available, or the timeout expires.
 * <p/>
 * Any method will be blocking if: <ul>
 * <li>It declares a return type other than {@code void} or {@code Future}, or</li>
 * <li>It declares an exception, or</li>
 * <li>The last two parameters are of type {@link TimeUnit} and {@link Long long}, or</li>
 * <li>The method is annotated with {@link Timeout @Timeout}</li>
 * </ul>
 * In other cases, the method is non-blocking and will return immediately after dispatching a command.
 * <p/>
 * This factory is thread safe once configured, and so are the gateways it creates.
 *
 * @author <NAME>
 * @since 2.0
 */
public class GatewayProxyFactory {

    private final CommandBus commandBus;
    private final RetryScheduler retryScheduler;
    private final List<MessageDispatchInterceptor<? super CommandMessage<?>>> dispatchInterceptors;
    private final List<CommandCallback<?, ?>> commandCallbacks;

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus           The CommandBus on which to dispatch the Command Messages
     * @param dispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    @SafeVarargs
    public GatewayProxyFactory(CommandBus commandBus,
                               MessageDispatchInterceptor<CommandMessage<?>>... dispatchInterceptors) {
        this(commandBus, null, dispatchInterceptors);
    }

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}. The given {@code retryScheduler} will reschedule commands for
     * dispatching if a previous attempt resulted in an exception.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus                  The CommandBus on which to dispatch the Command Messages
     * @param retryScheduler              The scheduler that will decide whether to reschedule commands, may be
     *                                    {@code null} to report failures without rescheduling
     * @param messageDispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    @SafeVarargs
    public GatewayProxyFactory(CommandBus commandBus, RetryScheduler retryScheduler,
                               MessageDispatchInterceptor<CommandMessage<?>>... messageDispatchInterceptors) {
        this(commandBus, retryScheduler, asList(messageDispatchInterceptors));
    }

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}. The given {@code retryScheduler} will reschedule commands for
     * dispatching if a previous attempt resulted in an exception.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus                  The CommandBus on which to dispatch the Command Messages
     * @param retryScheduler              The scheduler that will decide whether to reschedule commands, may be
     *                                    {@code null} to report failures without rescheduling
     * @param messageDispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    public GatewayProxyFactory(CommandBus commandBus, RetryScheduler retryScheduler,
                               List<MessageDispatchInterceptor<CommandMessage<?>>> messageDispatchInterceptors) {
        Assert.notNull(commandBus, "commandBus may not be null");
        this.retryScheduler = retryScheduler;
        this.commandBus = commandBus;
        if (messageDispatchInterceptors != null && !messageDispatchInterceptors.isEmpty()) {
            this.dispatchInterceptors = new CopyOnWriteArrayList<>(messageDispatchInterceptors);
        } else {
            this.dispatchInterceptors = new CopyOnWriteArrayList<>();
        }
        this.commandCallbacks = new CopyOnWriteArrayList<>();
    }

    /**
     * Creates a gateway instance for the given {@code gatewayInterface}. The returned instance is a Proxy that
     * implements that interface.
     *
     * @param gatewayInterface The interface declaring the gateway methods
     * @param <T>              The interface declaring the gateway methods
     * @return A Proxy implementation implementing the given interface
     */
    @SuppressWarnings("unchecked")
    public <T> T createGateway(Class<T> gatewayInterface) {
        Map<Method, InvocationHandler> dispatchers = new HashMap<>();
        for (Method gatewayMethod : gatewayInterface.getMethods()) {
            MetaDataExtractor[] extractors = extractMetaData(gatewayMethod.getParameterTypes(),
                                                             gatewayMethod.getParameterAnnotations());

            final Class<?>[] arguments = gatewayMethod.getParameterTypes();

            InvocationHandler dispatcher = new DispatchOnInvocationHandler(commandBus, retryScheduler,
                                                                           dispatchInterceptors, extractors,
                                                                           commandCallbacks, true);
            if (!Future.class.isAssignableFrom(gatewayMethod.getReturnType())
                    && !CompletionStage.class.isAssignableFrom(gatewayMethod.getReturnType())) {
                // no wrapping
                if (arguments.length >= 3
                        && TimeUnit.class.isAssignableFrom(arguments[arguments.length - 1])
                        && (Long.TYPE.isAssignableFrom(arguments[arguments.length - 2])
                        || Integer.TYPE.isAssignableFrom(arguments[arguments.length - 2]))) {
                    dispatcher = wrapToReturnWithTimeoutInArguments(dispatcher, arguments.length - 2,
                                                                    arguments.length - 1);
                } else {
                    Timeout timeout = gatewayMethod.getAnnotation(Timeout.class);
                    timeout = resolveTimeout(gatewayMethod, timeout);
                    if (timeout != null) {
                        dispatcher = wrapToReturnWithFixedTimeout(dispatcher, timeout.value(), timeout.unit());<commit_msg>Alternative fix to Object returns<commit_after>/*
 * Copyright (c) 2010-2016. Axon Framework
 *
 * 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.axonframework.commandhandling.gateway;

import org.axonframework.commandhandling.CommandBus;
import org.axonframework.commandhandling.CommandCallback;
import org.axonframework.commandhandling.CommandExecutionException;
import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.commandhandling.callbacks.FutureCallback;
import org.axonframework.common.Assert;
import org.axonframework.common.CollectionUtils;
import org.axonframework.common.ReflectionUtils;
import org.axonframework.messaging.MessageDispatchInterceptor;
import org.axonframework.messaging.annotation.MetaData;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static java.util.Arrays.asList;
import static org.axonframework.commandhandling.GenericCommandMessage.asCommandMessage;

/**
 * Factory that creates Gateway implementations from custom interface definitions. The behavior of the method is
 * defined by the parameters, declared exceptions and return type of the method.
 * <p/>
 * <em>Supported parameter types:</em><ul>
 * <li>The first parameter of the method is considered the payload of the message. If the first parameter is a Message
 * itself, a new message is created using the payload and metadata of the message passed as parameter.</li>
 * <li>Parameters that are annotated with {@link MetaData @MetaData} are will cause the parameter values to be added as
 * meta data values to the outgoing message.</li>
 * <li>If the last two parameters are of type {@link Long long} and {@link TimeUnit}, they are considered to represent
 * the timeout for the command. The method will block for as long as the command requires to execute, or until the
 * timeout expires.</li>
 * </ul>
 * <p/>
 * <em>Effect of return values</em><ul>
 * <li>{@code void} return types are always allowed. Unless another parameter makes the method blocking, void
 * methods are non-blocking by default.</li>
 * <li>Declaring a {@link Future} return type will always result in a non-blocking operation. A future is returned
 * that allows you to retrieve the execution's result at your own convenience. Note that declared exceptions and
 * timeouts are ignored.</li>
 * <li>Any other return type will cause the dispatch to block (optionally with timeout) until a result is
 * available</li>
 * </ul>
 * <p/>
 * <em>Effect of declared exceptions</em> <ul>
 * <li>Any checked exception declared on the method will cause it to block (optionally with timeout). If the command
 * results in a declared checked exception, that exception is thrown from the method.</li>
 * <li>Declaring a {@link TimeoutException} will throw that exception when a configured timeout expires. If no such
 * exception is declared, but a timeout is configured, the method will return {@code null}.</li>
 * <li>Declaring an {@link InterruptedException} will throw that exception when a thread blocked while waiting for a
 * response is interrupted. Not declaring the exception will have the method return {@code null} when a blocked
 * thread is interrupted. Note that when no InterruptedException is declared, the interrupt flag is set back on the
 * interrupted thread</li>
 * </ul>
 * <p/>
 * <em>Effect of unchecked exceptions</em> <ul>
 * <li>Any unchecked exception thrown during command handling will cause it to block. If the method is blocking (see
 * below) the
 * unchecked exception will be thrown from the method</li>
 * </ul>
 * <p/>
 * Finally, the {@link Timeout @Timeout} annotation can be used to define a timeout on a method. This will always cause
 * a method invocation to block until a response is available, or the timeout expires.
 * <p/>
 * Any method will be blocking if: <ul>
 * <li>It declares a return type other than {@code void} or {@code Future}, or</li>
 * <li>It declares an exception, or</li>
 * <li>The last two parameters are of type {@link TimeUnit} and {@link Long long}, or</li>
 * <li>The method is annotated with {@link Timeout @Timeout}</li>
 * </ul>
 * In other cases, the method is non-blocking and will return immediately after dispatching a command.
 * <p/>
 * This factory is thread safe once configured, and so are the gateways it creates.
 *
 * @author <NAME>
 * @since 2.0
 */
public class GatewayProxyFactory {

    private final CommandBus commandBus;
    private final RetryScheduler retryScheduler;
    private final List<MessageDispatchInterceptor<? super CommandMessage<?>>> dispatchInterceptors;
    private final List<CommandCallback<?, ?>> commandCallbacks;

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus           The CommandBus on which to dispatch the Command Messages
     * @param dispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    @SafeVarargs
    public GatewayProxyFactory(CommandBus commandBus,
                               MessageDispatchInterceptor<CommandMessage<?>>... dispatchInterceptors) {
        this(commandBus, null, dispatchInterceptors);
    }

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}. The given {@code retryScheduler} will reschedule commands for
     * dispatching if a previous attempt resulted in an exception.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus                  The CommandBus on which to dispatch the Command Messages
     * @param retryScheduler              The scheduler that will decide whether to reschedule commands, may be
     *                                    {@code null} to report failures without rescheduling
     * @param messageDispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    @SafeVarargs
    public GatewayProxyFactory(CommandBus commandBus, RetryScheduler retryScheduler,
                               MessageDispatchInterceptor<CommandMessage<?>>... messageDispatchInterceptors) {
        this(commandBus, retryScheduler, asList(messageDispatchInterceptors));
    }

    /**
     * Initialize the factory sending Commands to the given {@code commandBus}, optionally intercepting them with
     * given {@code dispatchInterceptors}. The given {@code retryScheduler} will reschedule commands for
     * dispatching if a previous attempt resulted in an exception.
     * <p/>
     * Note that the given {@code dispatchInterceptors} are applied only on commands sent through gateways that
     * have been created using this factory.
     *
     * @param commandBus                  The CommandBus on which to dispatch the Command Messages
     * @param retryScheduler              The scheduler that will decide whether to reschedule commands, may be
     *                                    {@code null} to report failures without rescheduling
     * @param messageDispatchInterceptors The interceptors to invoke before dispatching commands to the Command Bus
     */
    public GatewayProxyFactory(CommandBus commandBus, RetryScheduler retryScheduler,
                               List<MessageDispatchInterceptor<CommandMessage<?>>> messageDispatchInterceptors) {
        Assert.notNull(commandBus, "commandBus may not be null");
        this.retryScheduler = retryScheduler;
        this.commandBus = commandBus;
        if (messageDispatchInterceptors != null && !messageDispatchInterceptors.isEmpty()) {
            this.dispatchInterceptors = new CopyOnWriteArrayList<>(messageDispatchInterceptors);
        } else {
            this.dispatchInterceptors = new CopyOnWriteArrayList<>();
        }
        this.commandCallbacks = new CopyOnWriteArrayList<>();
    }

    /**
     * Creates a gateway instance for the given {@code gatewayInterface}. The returned instance is a Proxy that
     * implements that interface.
     *
     * @param gatewayInterface The interface declaring the gateway methods
     * @param <T>              The interface declaring the gateway methods
     * @return A Proxy implementation implementing the given interface
     */
    @SuppressWarnings("unchecked")
    public <T> T createGateway(Class<T> gatewayInterface) {
        Map<Method, InvocationHandler> dispatchers = new HashMap<>();
        for (Method gatewayMethod : gatewayInterface.getMethods()) {
            MetaDataExtractor[] extractors = extractMetaData(gatewayMethod.getParameterTypes(),
                                                             gatewayMethod.getParameterAnnotations());

            final Class<?>[] arguments = gatewayMethod.getParameterTypes();

            InvocationHandler dispatcher = new DispatchOnInvocationHandler(commandBus, retryScheduler,
                                                                           dispatchInterceptors, extractors,
                                                                           commandCallbacks, true);

            if( !(CompletableFuture.class.isAssignableFrom(gatewayMethod.getReturnType())
                    || Future.class.isAssignableFrom(gatewayMethod.getReturnType())
                    || CompletionStage.class.isAssignableFrom(gatewayMethod.getReturnType()))) {
                // no wrapping
                if (arguments.length >= 3
                        && TimeUnit.class.isAssignableFrom(arguments[arguments.length - 1])
                        && (Long.TYPE.isAssignableFrom(arguments[arguments.length - 2])
                        || Integer.TYPE.isAssignableFrom(arguments[arguments.length - 2]))) {
                    dispatcher = wrapToReturnWithTimeoutInArguments(dispatcher, arguments.length - 2,
                                                                    arguments.length - 1);
                } else {
                    Timeout timeout = gatewayMethod.getAnnotation(Timeout.class);
                    timeout = resolveTimeout(gatewayMethod, timeout);
                    if (timeout != null) {
                        dispatcher = wrapToReturnWithFixedTimeout(dispatcher, timeout.value(), timeout.unit());
{'fraction_non_alphanumeric': 0.07654054054054055, 'fraction_numerical': 0.0017297297297297297, 'mean_word_length': 3.1751218631521936, 'pattern_counts': {'":': 0, '<': 159, '<?xml version=': 0, '>': 161, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '537819', 'n_tokens_mistral': 5534, 'n_tokens_neox': 5305, 'n_words': 2162}