提交 895eeff5 编写于 作者: T Tomasz Bak

Dynamic proxy client prototype.

上级 4cfb6bc6
......@@ -2,6 +2,7 @@ package com.netflix.ribbonclientextensions;
import com.netflix.ribbonclientextensions.http.HttpRequestTemplate;
import com.netflix.ribbonclientextensions.typedclient.RibbonDynamicProxy;
import io.reactivex.netty.protocol.http.client.HttpClient;
public final class Ribbon {
......@@ -14,6 +15,6 @@ public final class Ribbon {
}
public static <I, O, T> T from(Class<T> contract, HttpClient<I, O> transportClient) {
return null;
return RibbonDynamicProxy.newInstance(contract, transportClient);
}
}
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.GET;
import com.netflix.ribbonclientextensions.typedclient.annotation.POST;
import com.netflix.ribbonclientextensions.typedclient.annotation.Path;
import com.netflix.ribbonclientextensions.typedclient.annotation.TemplateName;
import com.netflix.ribbonclientextensions.typedclient.annotation.Var;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import static java.lang.String.*;
/**
* Extracts information from Ribbon annotated method, to automatically populate the Ribbon request template.
* A few validations are performed as well:
* - a return type must be {@link com.netflix.ribbonclientextensions.RibbonRequest}
* - HTTP method must be always specified explicitly (there are no defaults)
* - only parameter with {@link com.netflix.ribbonclientextensions.typedclient.annotation.Content} annotation is allowed
*
* @author Tomasz Bak
*/
public class MethodTemplate {
public static enum HttpMethod {
GET,
POST
}
private static final MethodTemplate[] EMPTY_ARRAY = new MethodTemplate[]{};
private final String templateName;
private final HttpMethod httpMethod;
private final Method method;
private final String path;
private final String[] paramNames;
private final int[] valueIdxs;
private final int contentArgPosition;
public MethodTemplate(Method method) {
this.method = method;
this.httpMethod = extractHttpMethod();
this.templateName = extractTemplateName();
this.path = extractPathInfo();
Object[] nameIdxPair = extractParamNamesWithIndexes();
this.paramNames = (String[]) nameIdxPair[0];
this.valueIdxs = (int[]) nameIdxPair[1];
this.contentArgPosition = extractContentArgPosition();
verifyResultType();
}
public HttpMethod getHttpMethod() {
return httpMethod;
}
public String getTemplateName() {
return templateName;
}
public Method getMethod() {
return method;
}
public String getPath() {
return path;
}
public String getParamNames(int idx) {
return paramNames[idx];
}
public int getParamPosition(int idx) {
return valueIdxs[idx];
}
public int getParamSize() {
return paramNames.length;
}
public int getContentArgPosition() {
return contentArgPosition;
}
public static <T> MethodTemplate[] from(Class<T> clientInterface) {
List<MethodTemplate> list = new ArrayList<MethodTemplate>(clientInterface.getMethods().length);
for (Method m : clientInterface.getMethods()) {
list.add(new MethodTemplate(m));
}
return list.toArray(EMPTY_ARRAY);
}
private HttpMethod extractHttpMethod() {
Annotation annotation = method.getAnnotation(GET.class);
if (null != annotation) {
return HttpMethod.GET;
}
annotation = method.getAnnotation(POST.class);
if (null != annotation) {
return HttpMethod.POST;
}
throw new IllegalArgumentException(format(
"Method %s.%s does not specify HTTP method with @GET or @POST annotation",
method.getDeclaringClass().getSimpleName(), method.getName()));
}
private String extractTemplateName() {
TemplateName annotation = method.getAnnotation(TemplateName.class);
return (annotation != null) ? annotation.value() : null;
}
private String extractPathInfo() {
Path annotation = method.getAnnotation(Path.class);
return (annotation != null) ? annotation.value() : null;
}
private Object[] extractParamNamesWithIndexes() {
List<String> nameList = new ArrayList<String>();
List<Integer> idxList = new ArrayList<Integer>();
Annotation[][] params = method.getParameterAnnotations();
for (int i = 0; i < params.length; i++) {
for (Annotation a : params[i]) {
if (a.annotationType().equals(Var.class)) {
String name = ((Var) a).value();
nameList.add(name);
idxList.add(i);
}
}
}
int size = nameList.size();
String[] names = new String[size];
int[] idxs = new int[size];
for (int i = 0; i < size; i++) {
names[i] = nameList.get(i);
idxs[i] = idxList.get(i);
}
return new Object[]{names, idxs};
}
private int extractContentArgPosition() {
Annotation[][] params = method.getParameterAnnotations();
int pos = -1;
int count = 0;
for (int i = 0; i < params.length; i++) {
for (Annotation a : params[i]) {
if (a.annotationType().equals(Content.class)) {
pos = i;
count++;
}
}
}
if (count > 1) {
throw new IllegalArgumentException(format(
"Method %s.%s annotates multiple parameters as @Content - at most one is allowed ",
method.getDeclaringClass().getSimpleName(), method.getName()));
}
return pos;
}
private void verifyResultType() {
Class resultType = method.getReturnType();
if (resultType.isAssignableFrom(RibbonRequest.class)) {
return;
}
throw new IllegalArgumentException(format(
"Method %s.%s must return Void or RibbonRequest<T> type not %s",
method.getDeclaringClass().getSimpleName(), method.getName(), resultType.getSimpleName()));
}
}
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.RequestTemplate.RequestBuilder;
import com.netflix.ribbonclientextensions.Ribbon;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.http.HttpRequestTemplate;
import io.netty.buffer.ByteBuf;
import io.reactivex.netty.protocol.http.client.ContentSource;
import io.reactivex.netty.protocol.http.client.ContentSource.SingletonSource;
import io.reactivex.netty.protocol.http.client.HttpClient;
import io.reactivex.netty.protocol.http.client.RawContentSource.SingletonRawSource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @author Tomasz Bak
*/
public class MethodTemplateExecutor<O> {
private final MethodTemplate methodTemplate;
public MethodTemplateExecutor(MethodTemplate methodTemplate) {
this.methodTemplate = methodTemplate;
}
public <I> RibbonRequest<O> executeFromTemplate(HttpClient httpClient, Object[] args) {
HttpRequestTemplate<ByteBuf, ByteBuf> httpRequestTemplate =
Ribbon.newHttpRequestTemplate(methodTemplate.getTemplateName(), httpClient);
httpRequestTemplate.withMethod(methodTemplate.getHttpMethod().name());
if (methodTemplate.getPath() != null) {
httpRequestTemplate.withUri(methodTemplate.getPath());
}
if(methodTemplate.getContentArgPosition() >= 0) {
Object contentValue = args[methodTemplate.getContentArgPosition()];
httpRequestTemplate.withContentSource(new SingletonSource(contentValue));
}
RequestBuilder requestBuilder = httpRequestTemplate.requestBuilder();
int length = methodTemplate.getParamSize();
for (int i = 0; i < length; i++) {
String name = methodTemplate.getParamNames(i);
Object value = args[methodTemplate.getParamPosition(i)];
requestBuilder.withValue(name, value);
}
return requestBuilder.build();
}
public static <O> Map<Method, MethodTemplateExecutor<O>> from(Class clientInterface) {
Map<Method, MethodTemplateExecutor<O>> tgm = new HashMap<Method, MethodTemplateExecutor<O>>();
for (MethodTemplate mt : MethodTemplate.from(clientInterface)) {
tgm.put(mt.getMethod(), new MethodTemplateExecutor(mt));
}
return tgm;
}
}
package com.netflix.ribbonclientextensions.typedclient;
import java.lang.reflect.Method;
import static java.lang.String.*;
/**
* A collection of reflection helper methods.
*
* @author Tomasz Bak
*/
class ReflectUtil {
public static <T> Method methodByName(Class<T> aClass, String name) {
for (Method m : aClass.getMethods()) {
if (m.getName().equals(name)) {
return m;
}
}
return null;
}
public static Object executeOnInstance(Object object, Method method, Object[] args) {
Method targetMethod = methodByName(object.getClass(), method.getName());
if (targetMethod == null) {
throw new IllegalArgumentException(format(
"Signature of method %s is not compatible with the object %s",
method.getName(), object.getClass().getSimpleName()));
}
try {
return targetMethod.invoke(object, args);
} catch (Exception ex) {
throw new RuntimeException(format(
"Failed to execute method %s on object %s",
method.getName(), object.getClass().getSimpleName()), ex);
}
}
}
package com.netflix.ribbonclientextensions.typedclient;
import io.reactivex.netty.protocol.http.client.HttpClient;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
/**
* @author Tomasz Bak
*/
public class RibbonDynamicProxy<O> implements InvocationHandler {
private final Map<Method, MethodTemplateExecutor<O>> templateGeneratorMap;
private final HttpClient httpClient;
public RibbonDynamicProxy(Class<O> clientInterface, HttpClient httpClient) {
this.httpClient = httpClient;
this.templateGeneratorMap = MethodTemplateExecutor.from(clientInterface);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodTemplateExecutor template = templateGeneratorMap.get(method);
if (template != null) {
return template.executeFromTemplate(httpClient, args);
}
// This must be one of the Object methods. Lets run it on the handler itself.
return ReflectUtil.executeOnInstance(this, method, args);
}
@Override
public String toString() {
return "RibbonDynamicProxy{...}";
}
@SuppressWarnings("unchecked")
public static <T> T newInstance(Class<T> clientInterface, HttpClient httpClient) {
if (!clientInterface.isInterface()) {
throw new IllegalArgumentException(clientInterface.getName() + " is a class not interface");
}
return (T) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class[]{clientInterface},
new RibbonDynamicProxy<T>(clientInterface, httpClient)
);
}
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
public String key();
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Content {
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Hystrix {
public String name();
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface POST {
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Path {
public String value() default "";
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TemplateName {
public String value();
}
package com.netflix.ribbonclientextensions.typedclient.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Var {
public String value();
}
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.RequestTemplate.RequestBuilder;
import com.netflix.ribbonclientextensions.Ribbon;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.http.HttpRequestTemplate;
import com.netflix.ribbonclientextensions.typedclient.sample.Movie;
import com.netflix.ribbonclientextensions.typedclient.sample.SampleTypedMovieService;
import io.reactivex.netty.protocol.http.client.ContentSource;
import io.reactivex.netty.protocol.http.client.HttpClient;
import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Method;
import java.util.Map;
import static com.netflix.ribbonclientextensions.typedclient.ReflectUtil.*;
import static junit.framework.Assert.*;
import static org.easymock.EasyMock.*;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.*;
import static org.powermock.api.easymock.PowerMock.replay;
/**
* @author Tomasz Bak
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(Ribbon.class)
public class MethodTemplateExecutorTest {
@Test
public void testGetWithParameterRequest() throws Exception {
RibbonRequest ribbonRequestMock = createMock(RibbonRequest.class);
RequestBuilder requestBuilderMock = createMock(RequestBuilder.class);
expect(requestBuilderMock.withValue("id", "id123")).andReturn(requestBuilderMock);
expect(requestBuilderMock.build()).andReturn(ribbonRequestMock);
HttpClient httpClientMock = createMock(HttpClient.class);
HttpRequestTemplate httpRequestTemplateMock = createMock(HttpRequestTemplate.class);
expect(httpRequestTemplateMock.withMethod("GET")).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.withUri("/movies/{id}")).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.requestBuilder()).andReturn(requestBuilderMock);
mockStatic(Ribbon.class);
expect(Ribbon.newHttpRequestTemplate("findMovieById", httpClientMock)).andReturn(httpRequestTemplateMock);
replay(Ribbon.class, ribbonRequestMock, requestBuilderMock, httpClientMock, httpRequestTemplateMock);
MethodTemplate methodTemplate = new MethodTemplate(methodByName(SampleTypedMovieService.class, "findMovieById"));
MethodTemplateExecutor generator = new MethodTemplateExecutor(methodTemplate);
RibbonRequest ribbonRequest = generator.executeFromTemplate(httpClientMock, new Object[]{"id123"});
assertEquals(ribbonRequestMock, ribbonRequest);
}
@Test
public void testPostWithContentParameter() throws Exception {
RibbonRequest ribbonRequestMock = createMock(RibbonRequest.class);
HttpClient httpClientMock = createMock(HttpClient.class);
RequestBuilder requestBuilderMock = createMock(RequestBuilder.class);
expect(requestBuilderMock.build()).andReturn(ribbonRequestMock);
HttpRequestTemplate httpRequestTemplateMock = createMock(HttpRequestTemplate.class);
expect(httpRequestTemplateMock.withMethod("POST")).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.withUri("/movies")).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.withContentSource((ContentSource) EasyMock.anyObject())).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.requestBuilder()).andReturn(requestBuilderMock);
mockStatic(Ribbon.class);
expect(Ribbon.newHttpRequestTemplate("registerMovie", httpClientMock)).andReturn(httpRequestTemplateMock);
replay(Ribbon.class, ribbonRequestMock, requestBuilderMock, httpClientMock, httpRequestTemplateMock);
MethodTemplate methodTemplate = new MethodTemplate(methodByName(SampleTypedMovieService.class, "registerMovie"));
MethodTemplateExecutor generator = new MethodTemplateExecutor(methodTemplate);
RibbonRequest ribbonRequest = generator.executeFromTemplate(httpClientMock, new Object[]{new Movie()});
assertEquals(ribbonRequestMock, ribbonRequest);
}
@Test
public void testFromFactory() throws Exception {
Map<Method, MethodTemplateExecutor<Object>> executorMap = MethodTemplateExecutor.from(SampleTypedMovieService.class);
assertEquals(3, executorMap.size());
}
}
\ No newline at end of file
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.typedclient.sample.SampleBrokenTypedMovieService;
import com.netflix.ribbonclientextensions.typedclient.sample.SampleTypedMovieService;
import org.junit.Test;
import static com.netflix.ribbonclientextensions.typedclient.ReflectUtil.*;
import static org.junit.Assert.*;
/**
* @author Tomasz Bak
*/
public class MethodTemplateTest {
@Test
public void testGetWithOneParameter() throws Exception {
MethodTemplate template = new MethodTemplate(methodByName(SampleTypedMovieService.class, "findMovieById"));
assertEquals("findMovieById", template.getTemplateName());
assertEquals("/movies/{id}", template.getPath());
assertEquals("id", template.getParamNames(0));
assertEquals(0, template.getParamPosition(0));
}
@Test
public void testGetWithTwoParameters() throws Exception {
MethodTemplate template = new MethodTemplate(methodByName(SampleTypedMovieService.class, "findMovie"));
assertEquals("findMovie", template.getTemplateName());
assertEquals("/movies?name={name}&author={author}", template.getPath());
assertEquals("name", template.getParamNames(0));
assertEquals(0, template.getParamPosition(0));
assertEquals("author", template.getParamNames(1));
assertEquals(1, template.getParamPosition(1));
}
@Test
public void testFromFactory() throws Exception {
MethodTemplate[] methodTemplates = MethodTemplate.from(SampleTypedMovieService.class);
assertEquals(3, methodTemplates.length);
}
@Test(expected = IllegalArgumentException.class)
public void testDetectsInvalidResultType() throws Exception {
new MethodTemplate(methodByName(SampleBrokenTypedMovieService.class, "returnTypeNotRibbonRequest"));
}
@Test(expected = IllegalArgumentException.class)
public void testMissingHttpMethod() throws Exception {
new MethodTemplate(methodByName(SampleBrokenTypedMovieService.class, "missingHttpMethod"));
}
@Test(expected = IllegalArgumentException.class)
public void testMultipleContentParameters() throws Exception {
new MethodTemplate(methodByName(SampleBrokenTypedMovieService.class, "multipleContentParameters"));
}
}
\ No newline at end of file
package com.netflix.ribbonclientextensions.typedclient;
import org.junit.Test;
import java.lang.reflect.Method;
import static junit.framework.Assert.*;
/**
* @author Tomasz Bak
*/
public class ReflectUtilTest {
@Test
public void testMethodByName() throws Exception {
Method source = ReflectUtil.methodByName(String.class, "equals");
assertNotNull(source);
assertEquals("equals", source.getName());
assertNull(ReflectUtil.methodByName(String.class, "not_equals"));
}
@Test
public void testExecuteOnInstance() throws Exception {
Method source = ReflectUtil.methodByName(String.class, "equals");
Object obj = new Object();
assertEquals(Boolean.TRUE, ReflectUtil.executeOnInstance(obj, source, new Object[]{obj}));
assertEquals(Boolean.FALSE, ReflectUtil.executeOnInstance(obj, source, new Object[]{this}));
}
@Test(expected = IllegalArgumentException.class)
public void testExecuteNotExistingMethod() throws Exception {
Method source = ReflectUtil.methodByName(String.class, "getChars");
ReflectUtil.executeOnInstance(new Object(), source, new Object[]{});
}
}
\ No newline at end of file
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.typedclient.sample.Movie;
import com.netflix.ribbonclientextensions.typedclient.sample.SampleTypedMovieService;
import io.reactivex.netty.protocol.http.client.HttpClient;
import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import static com.netflix.ribbonclientextensions.typedclient.ReflectUtil.*;
import static org.easymock.EasyMock.*;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.*;
import static org.powermock.api.easymock.PowerMock.replay;
import static org.testng.Assert.*;
/**
* @author Tomasz Bak
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({RibbonDynamicProxy.class, MethodTemplateExecutor.class})
public class RibbonDynamicProxyTest {
@Test(expected = IllegalArgumentException.class)
public void testAcceptsInterfaceOnly() throws Exception {
RibbonDynamicProxy.newInstance(Object.class, null);
}
@Test
public void testTypedClientGetWithPathParameter() throws Exception {
HttpClient httpClientMock = createMock(HttpClient.class);
RibbonRequest ribbonRequestMock = createMock(RibbonRequest.class);
MethodTemplateExecutor tgMock = createMock(MethodTemplateExecutor.class);
expect(tgMock.executeFromTemplate((HttpClient) EasyMock.anyObject(), (Object[]) EasyMock.anyObject())).andReturn(ribbonRequestMock);
Map<Method, MethodTemplateExecutor<Object>> tgMap = new HashMap<Method, MethodTemplateExecutor<Object>>();
tgMap.put(methodByName(SampleTypedMovieService.class, "findMovieById"), tgMock);
mockStatic(MethodTemplateExecutor.class);
expect(MethodTemplateExecutor.from(SampleTypedMovieService.class)).andReturn(tgMap);
replay(MethodTemplateExecutor.class, tgMock, httpClientMock, ribbonRequestMock);
SampleTypedMovieService service = RibbonDynamicProxy.newInstance(SampleTypedMovieService.class, httpClientMock);
RibbonRequest<Movie> ribbonMovie = service.findMovieById("123");
assertNotNull(ribbonMovie);
}
@Test
public void testPlainObjectInvocations() throws Exception {
SampleTypedMovieService service = RibbonDynamicProxy.newInstance(SampleTypedMovieService.class, null);
assertFalse(service.equals(this));
assertEquals(service.toString(), "RibbonDynamicProxy{...}");
}
}
package com.netflix.ribbonclientextensions.typedclient.sample;
/**
* @author Tomasz Bak
*/
public class Movie {
}
package com.netflix.ribbonclientextensions.typedclient.sample;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.GET;
/**
* @author Tomasz Bak
*/
public interface SampleBrokenTypedMovieService {
@GET
public Movie returnTypeNotRibbonRequest();
public Movie missingHttpMethod();
@GET
public RibbonRequest<Void> multipleContentParameters(@Content Movie content1, @Content Movie content2);
}
package com.netflix.ribbonclientextensions.typedclient.sample;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.typedclient.annotation.Cache;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.GET;
import com.netflix.ribbonclientextensions.typedclient.annotation.Hystrix;
import com.netflix.ribbonclientextensions.typedclient.annotation.POST;
import com.netflix.ribbonclientextensions.typedclient.annotation.Path;
import com.netflix.ribbonclientextensions.typedclient.annotation.TemplateName;
import com.netflix.ribbonclientextensions.typedclient.annotation.Var;
/**
* @author Tomasz Bak
*/
@Hystrix(name = "movie")
public interface SampleTypedMovieService {
@TemplateName("findMovieById")
@Cache(key = "movie.{id}")
@GET
@Path("/movies/{id}")
RibbonRequest<Movie> findMovieById(@Var("id") String id);
@TemplateName("findMovie")
@Cache(key = "movie#name={name},author={author}")
@GET
@Path("/movies?name={name}&author={author}")
RibbonRequest<Movie> findMovie(@Var("name") String name, @Var("author") String author);
@TemplateName("registerMovie")
@Hystrix(name = "postMovie")
@POST
@Path("/movies")
RibbonRequest<Void> registerMovie(@Content Movie movie);
}
package com.netflix.ribbonclientextensions.typedclient.sample;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.typedclient.annotation.Cache;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.GET;
import com.netflix.ribbonclientextensions.typedclient.annotation.Hystrix;
import com.netflix.ribbonclientextensions.typedclient.annotation.POST;
import com.netflix.ribbonclientextensions.typedclient.annotation.Path;
import com.netflix.ribbonclientextensions.typedclient.annotation.Var;
import io.netty.buffer.ByteBuf;
/**
* @author Tomasz Bak
*/
@Hystrix(name = "movie")
public interface SampleUntypedMovieService {
@Cache(key = "movie.{id}")
@GET
@Path("/movies/{id}")
RibbonRequest<ByteBuf> findMovieById(@Var("id") String id);
@Cache(key = "movie#name={name},author={author}")
@GET
@Path("/movies?name={name}&author={author}")
RibbonRequest<ByteBuf> findMovie(@Var("name") String name, @Var("author") String author);
@Hystrix(name = "postMovie")
@POST
@Path("/movies")
RibbonRequest<Void> registerMovie(@Content ByteBuf movie);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册