ch.rasc.wampspring.method.InvocableWampHandlerMethod.java Source code

Java tutorial

Introduction

Here is the source code for ch.rasc.wampspring.method.InvocableWampHandlerMethod.java

Source

/**
 * Copyright 2002-2015 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 ch.rasc.wampspring.method;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.messaging.handler.HandlerMethod;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite;
import org.springframework.util.ReflectionUtils;

import ch.rasc.wampspring.message.WampMessage;

/**
 * Invokes the handler method for a given message after resolving its method argument
 * values through registered {@link HandlerMethodArgumentResolver}s.
 *
 * <p>
 * Use {@link #setMessageMethodArgumentResolvers(HandlerMethodArgumentResolver)} to
 * customize the list of argument resolvers.
 * <p>
 *
 * @author Rossen Stoyanchev
 * @author Juergen Hoeller
 * @author Ralph Schaer
 */
public class InvocableWampHandlerMethod extends HandlerMethod {

    private HandlerMethodArgumentResolver argumentResolvers = new HandlerMethodArgumentResolverComposite();

    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private final MethodParameterConverter methodParameterConverter;

    /**
     * Create an instance from a {@code HandlerMethod}.
     */
    public InvocableWampHandlerMethod(HandlerMethod handlerMethod,
            MethodParameterConverter methodParameterConverter) {
        super(handlerMethod);
        this.methodParameterConverter = methodParameterConverter;
    }

    public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolver argumentResolvers) {
        this.argumentResolvers = argumentResolvers;
    }

    /**
     * Set the ParameterNameDiscoverer for resolving parameter names when needed (e.g.
     * default request attribute name).
     * <p>
     * Default is a {@link org.springframework.core.DefaultParameterNameDiscoverer}.
     */
    public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
        this.parameterNameDiscoverer = parameterNameDiscoverer;
    }

    /**
     * Invoke the method with the given message.
     * @throws Exception raised if no suitable argument resolver can be found, or the
     * method raised an exception
     */
    public Object invoke(WampMessage message, Object... providedArgs) throws Exception {
        Object[] args = getMethodArgumentValues(message, providedArgs);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Resolved arguments: " + Arrays.asList(args));
        }
        Object returnValue = doInvoke(args);
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("Returned value: " + returnValue);
        }
        return returnValue;
    }

    /**
     * Get the method argument values for the current request.
     */
    private Object[] getMethodArgumentValues(WampMessage message, Object... providedArgs) throws Exception {

        MethodParameter[] parameters = getMethodParameters();
        Object[] args = new Object[parameters.length];
        int argIndex = 0;
        for (int i = 0; i < parameters.length; i++) {
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());

            if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    args[i] = this.argumentResolvers.resolveArgument(parameter, message);
                    continue;
                } catch (Exception ex) {
                    if (this.logger.isTraceEnabled()) {
                        this.logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
                    }
                    throw ex;
                }
            }

            if (providedArgs != null) {
                args[i] = this.methodParameterConverter.convert(parameter, providedArgs[argIndex]);
                if (args[i] != null) {
                    argIndex++;
                    continue;
                }
            }

            if (args[i] == null) {
                String error = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);
                throw new IllegalStateException(error);
            }
        }
        return args;
    }

    private String getArgumentResolutionErrorMessage(String message, int index) {
        MethodParameter param = getMethodParameters()[index];
        String msg = message + " [" + index + "] [type=" + param.getParameterType().getName() + "]";
        return getDetailedErrorMessage(msg);
    }

    /**
     * Adds HandlerMethod details such as the controller type and method signature to the
     * given error message.
     * @param message error message to append the HandlerMethod details to
     */
    protected String getDetailedErrorMessage(String message) {
        StringBuilder sb = new StringBuilder(message).append("\n");
        sb.append("HandlerMethod details: \n");
        sb.append("Bean [").append(getBeanType().getName()).append("]\n");
        sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
        return sb.toString();
    }

    /**
     * Invoke the handler method with the given argument values.
     */
    protected Object doInvoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        } catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex);
        } catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            } else if (targetException instanceof Error) {
                throw (Error) targetException;
            } else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            } else {
                String msg = getInvocationErrorMessage("Failed to invoke controller method", args);
                throw new IllegalStateException(msg, targetException);
            }
        }
    }

    /**
     * Assert that the target bean class is an instance of the class where the given
     * method is declared. In some cases the actual controller instance at request-
     * processing time may be a JDK dynamic proxy (lazy initialization, prototype beans,
     * and others). {@code @Controller}'s that require proxying should prefer class-based
     * proxy mechanisms.
     */
    private void assertTargetBean(Method method, Object targetBean, Object[] args) {
        Class<?> methodDeclaringClass = method.getDeclaringClass();
        Class<?> targetBeanClass = targetBean.getClass();
        if (!methodDeclaringClass.isAssignableFrom(targetBeanClass)) {
            String msg = "The mapped controller method class '" + methodDeclaringClass.getName()
                    + "' is not an instance of the actual controller bean instance '" + targetBeanClass.getName()
                    + "'. If the controller requires proxying "
                    + "(e.g. due to @Transactional), please use class-based proxying.";
            throw new IllegalStateException(getInvocationErrorMessage(msg, args));
        }
    }

    private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
        StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
        sb.append("Resolved arguments: \n");
        for (int i = 0; i < resolvedArgs.length; i++) {
            sb.append("[").append(i).append("] ");
            if (resolvedArgs[i] == null) {
                sb.append("[null] \n");
            } else {
                sb.append("[type=").append(resolvedArgs[i].getClass().getName()).append("] ");
                sb.append("[value=").append(resolvedArgs[i]).append("]\n");
            }
        }
        return sb.toString();
    }

}