Java tutorial
/** * 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(); } }