Browse Source
Just enough for a test with an @ResponseBody method that accepts an @RequestParam String arg and returning Publisher<String> or String. See RequestMappingIntegrationTests.pull/1111/head
17 changed files with 703 additions and 150 deletions
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method; |
||||
|
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
|
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public interface HandlerMethodArgumentResolver { |
||||
|
||||
boolean supportsParameter(MethodParameter parameter); |
||||
|
||||
Object resolveArgument(MethodParameter parameter, ServerHttpRequest request); |
||||
|
||||
} |
||||
@ -0,0 +1,184 @@
@@ -0,0 +1,184 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer; |
||||
import org.springframework.core.GenericTypeResolver; |
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.core.ParameterNameDiscoverer; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
|
||||
|
||||
/** |
||||
* 90% overlap with the existing one in spring-web except for the different |
||||
* HandlerMethodArgumentResolver contract. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class InvocableHandlerMethod extends HandlerMethod { |
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(); |
||||
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); |
||||
|
||||
|
||||
public InvocableHandlerMethod(HandlerMethod handlerMethod) { |
||||
super(handlerMethod); |
||||
} |
||||
|
||||
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { |
||||
this.argumentResolvers.clear(); |
||||
this.argumentResolvers.addAll(resolvers); |
||||
} |
||||
|
||||
|
||||
public Object invokeForRequest(ServerHttpRequest request, Object... providedArgs) throws Exception { |
||||
Object[] args = getMethodArgumentValues(request, providedArgs); |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Invoking [" + getBeanType().getSimpleName() + "." + |
||||
getMethod().getName() + "] method with arguments " + Arrays.asList(args)); |
||||
} |
||||
Object returnValue = doInvoke(args); |
||||
if (logger.isTraceEnabled()) { |
||||
logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]"); |
||||
} |
||||
return returnValue; |
||||
} |
||||
|
||||
private Object[] getMethodArgumentValues(ServerHttpRequest request, Object... providedArgs) throws Exception { |
||||
MethodParameter[] parameters = getMethodParameters(); |
||||
Object[] args = new Object[parameters.length]; |
||||
for (int i = 0; i < parameters.length; i++) { |
||||
MethodParameter parameter = parameters[i]; |
||||
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); |
||||
GenericTypeResolver.resolveParameterType(parameter, getBean().getClass()); |
||||
args[i] = resolveProvidedArgument(parameter, providedArgs); |
||||
if (args[i] != null) { |
||||
continue; |
||||
} |
||||
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { |
||||
if (resolver.supportsParameter(parameter)) { |
||||
try { |
||||
args[i] = resolver.resolveArgument(parameter, request); |
||||
break; |
||||
} |
||||
catch (Exception ex) { |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex); |
||||
} |
||||
throw ex; |
||||
} |
||||
} |
||||
} |
||||
if (args[i] == null) { |
||||
String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i); |
||||
throw new IllegalStateException(msg); |
||||
} |
||||
} |
||||
return args; |
||||
} |
||||
|
||||
private String getArgumentResolutionErrorMessage(String message, int index) { |
||||
MethodParameter param = getMethodParameters()[index]; |
||||
message += " [" + index + "] [type=" + param.getParameterType().getName() + "]"; |
||||
return getDetailedErrorMessage(message); |
||||
} |
||||
|
||||
protected String getDetailedErrorMessage(String message) { |
||||
return message + "\n" + "HandlerMethod details: \n" + |
||||
"Controller [" + getBeanType().getName() + "]\n" + |
||||
"Method [" + getBridgedMethod().toGenericString() + "]\n"; |
||||
} |
||||
|
||||
private Object resolveProvidedArgument(MethodParameter parameter, Object... providedArgs) { |
||||
if (providedArgs == null) { |
||||
return null; |
||||
} |
||||
for (Object providedArg : providedArgs) { |
||||
if (parameter.getParameterType().isInstance(providedArg)) { |
||||
return providedArg; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,79 @@
@@ -0,0 +1,79 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method.annotation; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.reactivestreams.Publisher; |
||||
import reactor.rx.Streams; |
||||
|
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.reactive.web.dispatch.HandlerAdapter; |
||||
import org.springframework.reactive.web.dispatch.HandlerResult; |
||||
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver; |
||||
import org.springframework.reactive.web.dispatch.method.InvocableHandlerMethod; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.reactive.web.http.ServerHttpResponse; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
|
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestMappingHandlerAdapter implements HandlerAdapter, InitializingBean { |
||||
|
||||
private List<HandlerMethodArgumentResolver> argumentResolvers; |
||||
|
||||
|
||||
public void setHandlerMethodArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { |
||||
this.argumentResolvers.clear(); |
||||
this.argumentResolvers.addAll(resolvers); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws Exception { |
||||
if (this.argumentResolvers == null) { |
||||
this.argumentResolvers = new ArrayList<>(); |
||||
this.argumentResolvers.add(new RequestParamArgumentResolver()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public boolean supports(Object handler) { |
||||
return HandlerMethod.class.equals(handler.getClass()); |
||||
} |
||||
|
||||
@Override |
||||
public Publisher<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response, |
||||
Object handler) { |
||||
|
||||
final InvocableHandlerMethod invocable = new InvocableHandlerMethod((HandlerMethod) handler); |
||||
invocable.setHandlerMethodArgumentResolvers(this.argumentResolvers); |
||||
|
||||
Object result; |
||||
try { |
||||
result = invocable.invokeForRequest(request); |
||||
} |
||||
catch (Exception ex) { |
||||
return Streams.fail(ex); |
||||
} |
||||
|
||||
return Streams.just(new HandlerResult(invocable, result)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method.annotation; |
||||
|
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.beans.factory.InitializingBean; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.core.annotation.AnnotationUtils; |
||||
import org.springframework.reactive.web.dispatch.HandlerMapping; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
import org.springframework.web.method.HandlerMethodSelector; |
||||
|
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestMappingHandlerMapping implements HandlerMapping, |
||||
ApplicationContextAware, InitializingBean { |
||||
|
||||
private static final Log logger = LogFactory.getLog(RequestMappingHandlerMapping.class); |
||||
|
||||
|
||||
private final Map<String, HandlerMethod> methodMap = new LinkedHashMap<>(); |
||||
|
||||
private ApplicationContext applicationContext; |
||||
|
||||
|
||||
@Override |
||||
public void setApplicationContext(ApplicationContext applicationContext) { |
||||
this.applicationContext = applicationContext; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public void afterPropertiesSet() throws Exception { |
||||
for (Object bean : this.applicationContext.getBeansOfType(Object.class).values()) { |
||||
detectHandlerMethods(bean); |
||||
} |
||||
} |
||||
|
||||
protected void detectHandlerMethods(final Object bean) { |
||||
final Class<?> beanType = bean.getClass(); |
||||
if (AnnotationUtils.findAnnotation(beanType, Controller.class) != null) { |
||||
HandlerMethodSelector.selectMethods(beanType, method -> { |
||||
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class); |
||||
if (annotation != null && annotation.value().length > 0) { |
||||
String path = annotation.value()[0]; |
||||
HandlerMethod handlerMethod = new HandlerMethod(bean, method); |
||||
if (logger.isInfoEnabled()) { |
||||
logger.info("Mapped \"" + path + "\" onto " + handlerMethod); |
||||
} |
||||
methodMap.put(path, handlerMethod); |
||||
} |
||||
return false; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object getHandler(ServerHttpRequest request) { |
||||
String path = request.getURI().getPath(); |
||||
HandlerMethod handlerMethod = this.methodMap.get(path); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Mapped " + path + " to [" + handlerMethod + "]"); |
||||
} |
||||
return handlerMethod; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method.annotation; |
||||
|
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.reactive.web.dispatch.method.HandlerMethodArgumentResolver; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.util.UriComponents; |
||||
import org.springframework.web.util.UriComponentsBuilder; |
||||
|
||||
|
||||
/** |
||||
* Support {@code @RequestParam} but for query params only. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestParamArgumentResolver implements HandlerMethodArgumentResolver { |
||||
|
||||
|
||||
@Override |
||||
public boolean supportsParameter(MethodParameter parameter) { |
||||
return parameter.hasParameterAnnotation(RequestParam.class); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object resolveArgument(MethodParameter param, ServerHttpRequest request) { |
||||
RequestParam annotation = param.getParameterAnnotation(RequestParam.class); |
||||
String name = (annotation.value().length() != 0 ? annotation.value() : param.getParameterName()); |
||||
UriComponents uriComponents = UriComponentsBuilder.fromUri(request.getURI()).build(); |
||||
return uriComponents.getQueryParams().getFirst(name); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,99 @@
@@ -0,0 +1,99 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method.annotation; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.nio.charset.Charset; |
||||
|
||||
import org.reactivestreams.Publisher; |
||||
import reactor.rx.Streams; |
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.annotation.AnnotatedElementUtils; |
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.reactive.web.dispatch.HandlerResult; |
||||
import org.springframework.reactive.web.dispatch.HandlerResultHandler; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.reactive.web.http.ServerHttpResponse; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
|
||||
|
||||
/** |
||||
* For now a simple {@code String} or {@code Publisher<String>} to |
||||
* "text/plain;charset=UTF-8" conversion. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class ResponseBodyResultHandler implements HandlerResultHandler, Ordered { |
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8"); |
||||
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE; |
||||
|
||||
|
||||
public void setOrder(int order) { |
||||
this.order = order; |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return this.order; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public boolean supports(HandlerResult result) { |
||||
Object handler = result.getHandler(); |
||||
if (handler instanceof HandlerMethod) { |
||||
Method method = ((HandlerMethod) handler).getMethod(); |
||||
return AnnotatedElementUtils.isAnnotated(method, ResponseBody.class.getName()); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
@Override |
||||
public Publisher<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, |
||||
HandlerResult result) { |
||||
|
||||
Object value = result.getValue(); |
||||
HandlerMethod handlerMethod = (HandlerMethod) result.getHandler(); |
||||
MethodParameter returnType = handlerMethod.getReturnValueType(value); |
||||
|
||||
if (value == null) { |
||||
return Streams.empty(); |
||||
} |
||||
|
||||
if (value instanceof String) { |
||||
response.getHeaders().setContentType(new MediaType("text", "plain", UTF_8)); |
||||
return response.writeWith(Streams.just(((String) value).getBytes(UTF_8))); |
||||
} |
||||
else if (value instanceof Publisher) { |
||||
Class<?> type = ResolvableType.forMethodParameter(returnType).resolveGeneric(0); |
||||
if (String.class.equals(type)) { |
||||
@SuppressWarnings("unchecked") |
||||
Publisher<String> content = (Publisher<String>) value; |
||||
return response.writeWith(Streams.wrap(content).map(value1 -> value1.getBytes(UTF_8))); |
||||
} |
||||
} |
||||
|
||||
return Streams.fail(new IllegalStateException("Return value type not supported: " + returnType)); |
||||
} |
||||
|
||||
} |
||||
@ -1,6 +1,8 @@
@@ -1,6 +1,8 @@
|
||||
log4j.rootCategory=INFO, stdout |
||||
log4j.logger.org.springframework.rx=DEBUG |
||||
log4j.rootCategory=WARN, stdout |
||||
|
||||
log4j.logger.org.springframework.reactive=DEBUG |
||||
log4j.logger.org.springframework.web=DEBUG |
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender |
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout |
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%25.25c{1}] <%t> - %m%n |
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] <%t> - %m%n |
||||
@ -1,118 +0,0 @@
@@ -1,118 +0,0 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch; |
||||
|
||||
import java.nio.charset.Charset; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import io.netty.buffer.ByteBuf; |
||||
import io.reactivex.netty.protocol.http.server.HttpServer; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.rx.Streams; |
||||
|
||||
import org.springframework.http.MediaType; |
||||
import org.springframework.reactive.web.http.ServerHttpRequest; |
||||
import org.springframework.reactive.web.http.ServerHttpResponse; |
||||
import org.springframework.reactive.web.http.rxnetty.RequestHandlerAdapter; |
||||
import org.springframework.web.context.support.StaticWebApplicationContext; |
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class DispatcherApp { |
||||
|
||||
public static void main(String[] args) { |
||||
|
||||
StaticWebApplicationContext wac = new StaticWebApplicationContext(); |
||||
wac.registerSingleton("handlerMapping", SimpleUrlHandlerMapping.class); |
||||
wac.registerSingleton("handlerAdapter", PlainTextHandlerAdapter.class); |
||||
wac.registerSingleton("resultHandler", PlainTextResultHandler.class); |
||||
wac.refresh(); |
||||
|
||||
SimpleUrlHandlerMapping handlerMapping = wac.getBean(SimpleUrlHandlerMapping.class); |
||||
handlerMapping.addHandler("/text", new HelloWorldTextHandler()); |
||||
|
||||
DispatcherHandler dispatcherHandler = new DispatcherHandler(); |
||||
dispatcherHandler.initStrategies(wac); |
||||
|
||||
RequestHandlerAdapter requestHandler = new RequestHandlerAdapter(dispatcherHandler); |
||||
HttpServer<ByteBuf, ByteBuf> server = HttpServer.newServer(8080); |
||||
server.start(requestHandler::handle); |
||||
server.awaitShutdown(); |
||||
} |
||||
|
||||
|
||||
private static class SimpleUrlHandlerMapping implements HandlerMapping { |
||||
|
||||
private final Map<String, Object> handlerMap = new HashMap<>(); |
||||
|
||||
|
||||
public void addHandler(String path, Object handler) { |
||||
this.handlerMap.put(path, handler); |
||||
} |
||||
|
||||
@Override |
||||
public Object getHandler(ServerHttpRequest request) { |
||||
return this.handlerMap.get(request.getURI().getPath()); |
||||
} |
||||
} |
||||
|
||||
private interface PlainTextHandler { |
||||
|
||||
Publisher<String> handle(ServerHttpRequest request, ServerHttpResponse response); |
||||
|
||||
} |
||||
|
||||
private static class HelloWorldTextHandler implements PlainTextHandler { |
||||
|
||||
@Override |
||||
public Publisher<String> handle(ServerHttpRequest request, ServerHttpResponse response) { |
||||
return Streams.just("Hello world."); |
||||
} |
||||
} |
||||
|
||||
private static class PlainTextHandlerAdapter implements HandlerAdapter { |
||||
|
||||
@Override |
||||
public boolean supports(Object handler) { |
||||
return PlainTextHandler.class.isAssignableFrom(handler.getClass()); |
||||
} |
||||
|
||||
@Override |
||||
public Publisher<HandlerResult> handle(ServerHttpRequest request, ServerHttpResponse response, Object handler) { |
||||
Publisher<String> publisher = ((PlainTextHandler) handler).handle(request, response); |
||||
return Streams.wrap(publisher).map(HandlerResult::new); |
||||
} |
||||
} |
||||
|
||||
private static class PlainTextResultHandler implements HandlerResultHandler { |
||||
|
||||
@Override |
||||
public boolean supports(HandlerResult result) { |
||||
Object value = result.getReturnValue(); |
||||
return (value != null && String.class.equals(value.getClass())); |
||||
} |
||||
|
||||
@Override |
||||
public Publisher<Void> handleResult(ServerHttpRequest request, ServerHttpResponse response, HandlerResult result) { |
||||
response.getHeaders().setContentType(MediaType.TEXT_PLAIN); |
||||
byte[] bytes = ((String) result.getReturnValue()).getBytes(Charset.forName("UTF-8")); |
||||
return response.writeWith(Streams.just(bytes)); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* 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 org.springframework.reactive.web.dispatch.method.annotation; |
||||
|
||||
|
||||
import java.net.URI; |
||||
import java.nio.charset.Charset; |
||||
|
||||
import org.junit.Test; |
||||
import org.reactivestreams.Publisher; |
||||
import reactor.rx.Streams; |
||||
|
||||
import org.springframework.http.RequestEntity; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.reactive.web.dispatch.DispatcherHandler; |
||||
import org.springframework.reactive.web.http.AbstractHttpHandlerIntegrationTests; |
||||
import org.springframework.reactive.web.http.HttpHandler; |
||||
import org.springframework.stereotype.Controller; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
import org.springframework.web.client.RestTemplate; |
||||
import org.springframework.web.context.support.StaticWebApplicationContext; |
||||
|
||||
import static org.junit.Assert.assertArrayEquals; |
||||
|
||||
/** |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class RequestMappingIntegrationTests extends AbstractHttpHandlerIntegrationTests { |
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8"); |
||||
|
||||
|
||||
@Override |
||||
protected HttpHandler createHttpHandler() { |
||||
|
||||
StaticWebApplicationContext wac = new StaticWebApplicationContext(); |
||||
wac.registerSingleton("handlerMapping", RequestMappingHandlerMapping.class); |
||||
wac.registerSingleton("handlerAdapter", RequestMappingHandlerAdapter.class); |
||||
wac.registerSingleton("responseBodyResultHandler", ResponseBodyResultHandler.class); |
||||
wac.registerSingleton("controller", TestController.class); |
||||
wac.refresh(); |
||||
|
||||
DispatcherHandler dispatcherHandler = new DispatcherHandler(); |
||||
dispatcherHandler.setApplicationContext(wac); |
||||
return dispatcherHandler; |
||||
} |
||||
|
||||
@Test |
||||
public void helloWithQueryParam() throws Exception { |
||||
|
||||
RestTemplate restTemplate = new RestTemplate(); |
||||
|
||||
URI url = new URI("http://localhost:" + port + "/param?name=George"); |
||||
RequestEntity<Void> request = RequestEntity.get(url).build(); |
||||
ResponseEntity<byte[]> response = restTemplate.exchange(request, byte[].class); |
||||
|
||||
assertArrayEquals("Hello George!".getBytes(UTF_8), response.getBody()); |
||||
} |
||||
|
||||
|
||||
@Controller |
||||
@SuppressWarnings("unused") |
||||
private static class TestController { |
||||
|
||||
@RequestMapping("/param") |
||||
@ResponseBody |
||||
public Publisher<String> handleWithParam(@RequestParam String name) { |
||||
return Streams.just("Hello ", name, "!"); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue