Created
December 12, 2017 09:29
-
-
Save miensol/cca73d158ce8e7664ed653a30fc8dc70 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CommandInvocableHandlerMethod(private val handlerMethod: HandlerMethod, | |
private val requestCommandFactory: RequestCommandFactory, | |
private val configuration: RequestCommandConfiguration) : ServletInvocableHandlerMethod(handlerMethod) { | |
private lateinit var returnValueHandlers: HandlerMethodReturnValueHandlerComposite | |
@Trace(dispatcher = true) | |
override fun invokeForRequest(request: NativeWebRequest?, mavContainer: ModelAndViewContainer?, vararg providedArgs: Any?): Any { | |
// same as super.invokeForRequest(request, mavContainer, *providedArgs) | |
//but with request passed to do invoke | |
val args = this.getMethodArgumentValuesCallable.invoke(request, mavContainer, providedArgs) | |
val result = doInvokeWithRequest(request, args) | |
return result | |
} | |
private fun doInvokeWithRequest(request: NativeWebRequest?, args: Array<out Any?>?): Any { | |
val nativeRequest = request?.getNativeRequest(HttpServletRequest::class.java) | |
// If the response has already set error status code tomcat will not wait for async result | |
// not sure how we should only handle DispatcherType.REQUEST | |
return if (nativeRequest != null && nativeRequest.dispatcherType == DispatcherType.REQUEST) { | |
val callSuper = Callable { | |
super.doInvoke(*(args ?: emptyArray())) | |
} | |
val job = callSuper | |
val context = RequestCommandContext(configuration, handlerMethod, SecurityContextHolder.getContext(), job) | |
val result = requestCommandFactory.createSingle(context) | |
MonoDeferredResult(result) | |
} else { | |
super.doInvoke(*(args ?: emptyArray())) | |
} | |
} | |
override fun setHandlerMethodReturnValueHandlers(returnValueHandlers: HandlerMethodReturnValueHandlerComposite?) { | |
this.returnValueHandlers = returnValueHandlers!! | |
super.setHandlerMethodReturnValueHandlers(returnValueHandlers) | |
} | |
override fun wrapConcurrentResult(result: Any?): ServletInvocableHandlerMethod { | |
return ConcurrentResultHandlerMethod(result, ConcurrentResultMethodParameter(result)) | |
} | |
private val getMethodArgumentValuesMethod = InvocableHandlerMethod::class.members | |
.single { it.name == "getMethodArgumentValues" } | |
.apply { isAccessible = true } | |
//Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) | |
private val getMethodArgumentValuesCallable: (request: NativeWebRequest?, mavContainer: ModelAndViewContainer?, args: Array<out Any?>) -> Array<out Any?>? | |
= { request, mavContainer, args -> | |
val receiver = this@CommandInvocableHandlerMethod | |
try { | |
getMethodArgumentValuesMethod.call(receiver, request, mavContainer, args) as Array<out Any?>? | |
} catch (ex: InvocationTargetException) { | |
LOG.debug("Invocation failed for $handlerMethod") | |
throw ex.cause ?: ex | |
} | |
} | |
private inner class ConcurrentResultHandlerMethod(result: Any?, returnType: CommandInvocableHandlerMethod.ConcurrentResultMethodParameter) : ServletInvocableHandlerMethod(Callable { | |
if (result is Exception) { | |
throw result | |
} else if (result is Throwable) { | |
throw NestedServletException("Async processing failed", result) | |
} | |
result | |
}, CALLABLE_METHOD) { | |
private val _returnType: MethodParameter | |
init { | |
setHandlerMethodReturnValueHandlers([email protected]) | |
this._returnType = returnType | |
} | |
/** | |
* Bridge to actual controller type-level annotations. | |
*/ | |
override fun getBeanType(): Class<*>? { | |
return [email protected]() | |
} | |
/** | |
* Bridge to actual return value or generic type within w the declared | |
* async return type, e.g. Foo instead of `DeferredResult<Foo>`. | |
*/ | |
override fun getReturnValueType(returnValue: Any?): MethodParameter? { | |
return this._returnType | |
} | |
/** | |
* Bridge to controller method-level annotations. | |
*/ | |
override fun <A : Annotation> getMethodAnnotation(annotationType: Class<A>): A? { | |
return [email protected](annotationType) | |
} | |
// next spring version | |
/** | |
* Bridge to controller method-level annotations. | |
*/ | |
override fun <A : Annotation> hasMethodAnnotation(annotationType: Class<A>): Boolean { | |
return [email protected](annotationType) | |
} | |
} | |
/** | |
* MethodParameter subclass based on the actual return value type or if | |
* that's null falling back on the generic type within the declared async | |
* return type, e.g. Foo instead of `DeferredResult<Foo>`. | |
*/ | |
private inner class ConcurrentResultMethodParameter : HandlerMethod.HandlerMethodParameter { | |
private val returnValue: Any? | |
private val returnType: ResolvableType? | |
constructor(returnValue: Any?) : super(-1) { | |
this.returnValue = returnValue | |
this.returnType = if (returnValue is DeferredResult<*> || returnValue is Callable<*> || returnValue is Future<*>) { | |
val genericParameterType = super.getGenericParameterType() | |
ResolvableType.forType(genericParameterType).getGeneric(0) | |
} else { | |
null | |
} | |
} | |
constructor(original: ConcurrentResultMethodParameter) : super(original) { | |
this.returnValue = original.returnValue | |
this.returnType = original.returnType | |
} | |
override fun getParameterType(): Class<*>? { | |
if (this.returnValue != null) { | |
return this.returnValue.javaClass | |
} | |
if (this.returnType != null && ResolvableType.NONE != this.returnType) { | |
return this.returnType.resolve() | |
} | |
return super.getParameterType() | |
} | |
override fun getGenericParameterType(): Type? { | |
//This might not be correct, but from what I've checked works as expected | |
return this.returnType?.type ?: parameterType | |
} | |
// next spring version | |
override fun clone(): ConcurrentResultMethodParameter { | |
return ConcurrentResultMethodParameter(this) | |
} | |
} | |
companion object { | |
val CALLABLE_METHOD = Callable<*>::call.javaMethod | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment