提交 c9eca011 编写于 作者: T Tomasz Bak

EVCache provider implementation and integration with dynamic proxy.

上级 5c02176e
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.evache;
/**
* @author Tomasz Bak
*/
public class CacheFaultException extends RuntimeException {
private static final long serialVersionUID = -1672764803141328757L;
public CacheFaultException(String message, Throwable cause) {
super(message, cause);
}
}
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.evache;
import com.netflix.evcache.EVCacheTranscoder;
/**
* @author Tomasz Bak
*/
public class EvCacheOptions {
private final String appName;
private final String cacheName;
private final boolean enableZoneFallback;
private final int timeToLive;
private final EVCacheTranscoder<?> transcoder;
private final String cacheKeyTemplate;
public EvCacheOptions(String appName, String cacheName, boolean enableZoneFallback, int timeToLive,
EVCacheTranscoder<?> transcoder, String cacheKeyTemplate) {
this.appName = appName;
this.cacheName = cacheName;
this.enableZoneFallback = enableZoneFallback;
this.timeToLive = timeToLive;
this.transcoder = transcoder;
this.cacheKeyTemplate = cacheKeyTemplate;
}
public String getAppName() {
return appName;
}
public String getCacheName() {
return cacheName;
}
public boolean isEnableZoneFallback() {
return enableZoneFallback;
}
public int getTimeToLive() {
return timeToLive;
}
public EVCacheTranscoder<?> getTranscoder() {
return transcoder;
}
public String getCacheKeyTemplate() {
return cacheKeyTemplate;
}
}
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.evache;
import com.netflix.evcache.EVCache;
import com.netflix.evcache.EVCacheException;
import com.netflix.ribbonclientextensions.CacheProvider;
import rx.Observable;
import java.util.Map;
import java.util.concurrent.Future;
/**
* @author Tomasz Bak
*/
public class EvCacheProvider<T> implements CacheProvider<T> {
private final EvCacheOptions options;
private final EVCache evCache;
public EvCacheProvider(EvCacheOptions options) {
this.options = options;
EVCache.Builder builder = new EVCache.Builder();
if (options.isEnableZoneFallback()) {
builder.enableZoneFallback();
}
builder.setDefaultTTL(options.getTimeToLive());
builder.setAppName(options.getAppName());
builder.setCacheName(options.getCacheName());
evCache = builder.build();
}
@SuppressWarnings("unchecked")
@Override
public Observable<T> get(String key, Map<String, Object> requestProperties) {
Future<T> getFuture;
try {
if (options.getTranscoder() == null) {
getFuture = evCache.getAsynchronous(key);
} else {
getFuture = (Future<T>) evCache.getAsynchronous(key, options.getTranscoder());
}
} catch (EVCacheException e) {
return Observable.error(new CacheFaultException("EVCache exception when getting value for key " + key, e));
}
return Observable.from(getFuture);
}
}
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.evache.EvCacheProvider;
import java.util.HashMap;
import java.util.Map;
/**
* @author Tomasz Bak
*/
public class EvCacheProviderPool {
private final Map<CacheId, EvCacheProvider<?>> pool;
public EvCacheProviderPool(MethodTemplate[] methodTemplates) {
pool = createEvCachePool(methodTemplates);
}
public CacheProvider<?> getMatching(EvCacheOptions evCacheOptions) {
return pool.get(new CacheId(evCacheOptions.getAppName(), evCacheOptions.getCacheName()));
}
private static Map<CacheId, EvCacheProvider<?>> createEvCachePool(MethodTemplate[] methodTemplates) {
Map<CacheId, EvCacheProvider<?>> newPool = new HashMap<CacheId, EvCacheProvider<?>>();
for (MethodTemplate template : methodTemplates) {
EvCacheOptions options = template.getEvCacheOptions();
if (options != null) {
CacheId id = new CacheId(options.getAppName(), options.getCacheName());
if (!newPool.containsKey(id)) {
newPool.put(id, new EvCacheProvider(options));
}
}
}
return newPool;
}
private static final class CacheId {
private final String appName;
private final String cacheName;
CacheId(String appName, String cacheName) {
this.appName = appName;
this.cacheName = cacheName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CacheId cacheId = (CacheId) o;
if (!appName.equals(cacheId.appName)) {
return false;
}
return cacheName.equals(cacheId.cacheName);
}
@Override
public int hashCode() {
int result = appName.hashCode();
result = 31 * result + cacheName.hashCode();
return result;
}
}
}
......@@ -15,15 +15,18 @@
*/
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.evcache.EVCacheTranscoder;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.CacheProviderFactory;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.http.HttpResponseValidator;
import com.netflix.ribbonclientextensions.hystrix.FallbackHandler;
import com.netflix.ribbonclientextensions.typedclient.annotation.CacheProviders;
import com.netflix.ribbonclientextensions.typedclient.annotation.CacheProviders.Provider;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.ContentTransformerClass;
import com.netflix.ribbonclientextensions.typedclient.annotation.EvCache;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http.Header;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http.HttpMethod;
......@@ -67,6 +70,7 @@ public class MethodTemplate {
private final FallbackHandler<?> hystrixFallbackHandler;
private final HttpResponseValidator hystrixResponseValidator;
private final Map<String, CacheProvider<?>> cacheProviders;
private final EvCacheOptions evCacheOptions;
public MethodTemplate(Method method) {
this.method = method;
......@@ -84,6 +88,7 @@ public class MethodTemplate {
hystrixFallbackHandler = values.hystrixFallbackHandler;
hystrixResponseValidator = values.hystrixResponseValidator;
cacheProviders = Collections.unmodifiableMap(values.cacheProviders);
evCacheOptions = values.evCacheOptions;
}
public HttpMethod getHttpMethod() {
......@@ -146,6 +151,10 @@ public class MethodTemplate {
return cacheProviders;
}
public EvCacheOptions getEvCacheOptions() {
return evCacheOptions;
}
public static <T> MethodTemplate[] from(Class<T> clientInterface) {
List<MethodTemplate> list = new ArrayList<MethodTemplate>(clientInterface.getMethods().length);
for (Method m : clientInterface.getMethods()) {
......@@ -170,6 +179,7 @@ public class MethodTemplate {
private HttpResponseValidator hystrixResponseValidator;
public final Map<String, String> headers = new HashMap<String, String>();
private final Map<String, CacheProvider<?>> cacheProviders = new HashMap<String, CacheProvider<?>>();
private EvCacheOptions evCacheOptions;
private MethodAnnotationValues(Method method) {
this.method = method;
......@@ -181,6 +191,7 @@ public class MethodTemplate {
extractResultType();
extractHystrixHandlers();
extractCacheProviders();
extractEvCacheOptions();
}
private void extractCacheProviders() {
......@@ -291,5 +302,30 @@ public class MethodTemplate {
ParameterizedType returnType = (ParameterizedType) method.getGenericReturnType();
resultType = (Class<?>) returnType.getActualTypeArguments()[0];
}
private void extractEvCacheOptions() {
EvCache annotation = method.getAnnotation(EvCache.class);
if (annotation == null) {
return;
}
Class<? extends EVCacheTranscoder<?>>[] transcoderClasses = annotation.transcoder();
EVCacheTranscoder<?> transcoder;
if (transcoderClasses.length == 0) {
transcoder = null;
} else if (transcoderClasses.length > 1) {
throw new RibbonTypedClientException("Multiple transcoders defined on method " + method.getName());
} else {
transcoder = Utils.newInstance(transcoderClasses[0]);
}
evCacheOptions = new EvCacheOptions(
annotation.appName(),
annotation.name(),
annotation.enableZoneFallback(),
annotation.ttl(),
transcoder,
annotation.cacheKeyTemplate());
}
}
}
......@@ -17,6 +17,8 @@ package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.evache.EvCacheProvider;
import com.netflix.ribbonclientextensions.http.HttpRequestBuilder;
import com.netflix.ribbonclientextensions.http.HttpRequestTemplate;
import com.netflix.ribbonclientextensions.http.HttpResourceGroup;
......@@ -48,10 +50,12 @@ public class MethodTemplateExecutor {
private final HttpResourceGroup httpResourceGroup;
private final MethodTemplate methodTemplate;
private final HttpRequestTemplate<?> httpRequestTemplate;
private final EvCacheProviderPool evCacheProviderPool;
public MethodTemplateExecutor(HttpResourceGroup httpResourceGroup, MethodTemplate methodTemplate) {
public MethodTemplateExecutor(HttpResourceGroup httpResourceGroup, MethodTemplate methodTemplate, EvCacheProviderPool evCacheProviderPool) {
this.httpResourceGroup = httpResourceGroup;
this.methodTemplate = methodTemplate;
this.evCacheProviderPool = evCacheProviderPool;
httpRequestTemplate = createHttpRequestTemplate();
}
......@@ -108,13 +112,17 @@ public class MethodTemplateExecutor {
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
@SuppressWarnings({"rawtypes", "unchecked", "OverlyStrongTypeCast"})
private void withCacheProviders(HttpRequestTemplate<?> httpRequestTemplate) {
if (methodTemplate.getCacheProviders() != null) {
for (Map.Entry<String, CacheProvider<?>> entry : methodTemplate.getCacheProviders().entrySet()) {
httpRequestTemplate.addCacheProvider(entry.getKey(), (CacheProvider) entry.getValue());
}
}
EvCacheOptions evCacheOptions = methodTemplate.getEvCacheOptions();
if (evCacheOptions != null) {
httpRequestTemplate.addCacheProvider(evCacheOptions.getCacheKeyTemplate(), (EvCacheProvider) evCacheProviderPool.getMatching(evCacheOptions));
}
}
private void withParameters(HttpRequestBuilder<?> requestBuilder, Object[] args) {
......@@ -145,9 +153,11 @@ public class MethodTemplateExecutor {
}
public static Map<Method, MethodTemplateExecutor> from(HttpResourceGroup httpResourceGroup, Class<?> clientInterface) {
MethodTemplate[] methodTemplates = MethodTemplate.from(clientInterface);
EvCacheProviderPool evCacheProviderPool = new EvCacheProviderPool(methodTemplates);
Map<Method, MethodTemplateExecutor> tgm = new HashMap<Method, MethodTemplateExecutor>();
for (MethodTemplate mt : MethodTemplate.from(clientInterface)) {
tgm.put(mt.getMethod(), new MethodTemplateExecutor(httpResourceGroup, mt));
for (MethodTemplate mt : methodTemplates) {
tgm.put(mt.getMethod(), new MethodTemplateExecutor(httpResourceGroup, mt, evCacheProviderPool));
}
return tgm;
}
......
......@@ -26,19 +26,19 @@ import java.util.Map;
* @author Tomasz Bak
*/
public class RibbonDynamicProxy<T> implements InvocationHandler {
private final Map<Method, MethodTemplateExecutor> templateGeneratorMap;
private final Map<Method, MethodTemplateExecutor> templateExecutorMap;
public RibbonDynamicProxy(Class<T> clientInterface, HttpResourceGroup httpResourceGroup) {
ClassTemplate<T> classTemplate = ClassTemplate.from(clientInterface);
if (httpResourceGroup == null) {
httpResourceGroup = new HttpResourceGroupFactory<T>(classTemplate).createResourceGroup();
}
templateGeneratorMap = MethodTemplateExecutor.from(httpResourceGroup, clientInterface);
templateExecutorMap = MethodTemplateExecutor.from(httpResourceGroup, clientInterface);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodTemplateExecutor template = templateGeneratorMap.get(method);
MethodTemplateExecutor template = templateExecutorMap.get(method);
if (template != null) {
return template.executeFromTemplate(args);
}
......
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.typedclient.annotation;
import com.netflix.evcache.EVCacheTranscoder;
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 EvCache {
String name();
String appName();
String cacheKeyTemplate();
int ttl() default 100;
boolean enableZoneFallback() default true;
Class<? extends EVCacheTranscoder<?>>[] transcoder() default {};
}
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.evache;
import com.netflix.evcache.EVCache;
import com.netflix.evcache.EVCacheException;
import com.netflix.evcache.EVCacheImpl;
import com.netflix.evcache.EVCacheTranscoder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import rx.Notification;
import rx.Observable;
import java.util.concurrent.Future;
import static junit.framework.Assert.*;
import static org.easymock.EasyMock.*;
import static org.powermock.api.easymock.PowerMock.*;
/**
* @author Tomasz Bak
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({EVCache.Builder.class, EVCacheImpl.class})
public class EvCacheProviderTest {
@Mock
private EVCacheImpl evCacheImplMock;
@Mock
private Future<String> cacheFutureMock;
@Mock
private EVCacheTranscoder<String> transcoderMock;
@Before
public void setUp() throws Exception {
PowerMock.mockStatic(EVCacheImpl.class);
expectNew(EVCacheImpl.class,
new Class[]{String.class, String.class, int.class, EVCacheTranscoder.class, boolean.class},
anyObject(String.class), anyObject(String.class), anyInt(), anyObject(EVCacheTranscoder.class), anyBoolean()
).andReturn(evCacheImplMock);
}
@Test
public void testAsynchronousAccessFromCache() throws Exception {
expect(evCacheImplMock.<String>getAsynchronous("test1")).andReturn(cacheFutureMock);
expect(cacheFutureMock.get()).andReturn("value1");
replayAll();
EvCacheOptions options = new EvCacheOptions("testApp", "test-cache", true, 100, null, "test{id}");
EvCacheProvider<Object> cacheProvider = new EvCacheProvider<Object>(options);
Observable<Object> cacheValue = cacheProvider.get("test1", null);
assertEquals("value1", cacheValue.toBlocking().first());
}
@Test
public void testAsynchronousAccessWithTranscoderFromCache() throws Exception {
expect(evCacheImplMock.getAsynchronous("test1", transcoderMock)).andReturn(cacheFutureMock);
expect(cacheFutureMock.get()).andReturn("value1");
replayAll();
EvCacheOptions options = new EvCacheOptions("testApp", "test-cache", true, 100, transcoderMock, "test{id}");
EvCacheProvider<Object> cacheProvider = new EvCacheProvider<Object>(options);
Observable<Object> cacheValue = cacheProvider.get("test1", null);
assertEquals("value1", cacheValue.toBlocking().first());
}
@Test
public void testFailedAsynchronousAccessFromCache() throws Exception {
expect(evCacheImplMock.<String>getAsynchronous("test1")).andThrow(new EVCacheException("cache error"));
replayAll();
EvCacheOptions options = new EvCacheOptions("testApp", "test-cache", true, 100, null, "test{id}");
EvCacheProvider<Object> cacheProvider = new EvCacheProvider<Object>(options);
Observable<Object> cacheValue = cacheProvider.get("test1", null);
Notification<Object> notification = cacheValue.materialize().toBlocking().first();
assertTrue(notification.getThrowable() instanceof CacheFaultException);
}
}
\ No newline at end of file
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.evache.EvCacheProvider;
import com.netflix.ribbonclientextensions.typedclient.sample.MovieServiceInterfaces.SampleMovieService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static junit.framework.Assert.*;
import static org.easymock.EasyMock.*;
import static org.powermock.api.easymock.PowerMock.*;
/**
* @author Tomasz Bak
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({EvCacheProviderPool.class})
public class EvCacheProviderPoolTest {
@Mock
private EvCacheProvider evCacheProviderMock;
@Before
public void setUp() throws Exception {
mockStatic(EvCacheProvider.class);
expectNew(EvCacheProvider.class, new Class[]{EvCacheOptions.class}, anyObject(EvCacheOptions.class)).andReturn(evCacheProviderMock);
}
@Test
public void testCreate() throws Exception {
replayAll();
EvCacheProviderPool pool = new EvCacheProviderPool(MethodTemplate.from(SampleMovieService.class));
MethodTemplate findById = new MethodTemplate(Utils.methodByName(SampleMovieService.class, "findMovieById"));
CacheProvider<?> cacheProvider = pool.getMatching(findById.getEvCacheOptions());
assertEquals(evCacheProviderMock, cacheProvider);
}
}
\ No newline at end of file
......@@ -2,6 +2,8 @@ package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.RibbonRequest;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.evache.EvCacheProvider;
import com.netflix.ribbonclientextensions.http.HttpRequestBuilder;
import com.netflix.ribbonclientextensions.http.HttpRequestTemplate;
import com.netflix.ribbonclientextensions.http.HttpResourceGroup;
......@@ -16,6 +18,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Method;
......@@ -31,6 +34,7 @@ import static org.powermock.api.easymock.PowerMock.*;
* @author Tomasz Bak
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({MethodTemplateExecutor.class})
public class MethodTemplateExecutorTest {
@Mock
......@@ -45,6 +49,12 @@ public class MethodTemplateExecutorTest {
@Mock
private HttpResourceGroup httpResourceGroupMock = createMock(HttpResourceGroup.class);
@Mock
private EvCacheProviderPool evCacheProviderPoolMock = createMock(EvCacheProviderPool.class);
@Mock
private EvCacheProvider evCacheProviderMock = createMock(EvCacheProvider.class);
@Before
public void setUp() throws Exception {
expect(requestBuilderMock.build()).andReturn(ribbonRequestMock);
......@@ -64,6 +74,8 @@ public class MethodTemplateExecutorTest {
expect(httpRequestTemplateMock.withFallbackProvider(anyObject(MovieFallbackHandler.class))).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.withResponseValidator(anyObject(SampleHttpResponseValidator.class))).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.addCacheProvider(anyObject(String.class), anyObject(CacheProvider.class))).andReturn(httpRequestTemplateMock);
expect(httpRequestTemplateMock.addCacheProvider(anyObject(String.class), anyObject(EvCacheProvider.class))).andReturn(httpRequestTemplateMock);
expect(evCacheProviderPoolMock.getMatching(anyObject(EvCacheOptions.class))).andReturn(evCacheProviderMock);
replayAll();
......@@ -130,6 +142,10 @@ public class MethodTemplateExecutorTest {
@Test
public void testFromFactory() throws Exception {
mockStatic(EvCacheProviderPool.class);
expectNew(EvCacheProviderPool.class, new Class[]{MethodTemplate[].class}, anyObject(MethodTemplate[].class)).andReturn(evCacheProviderPoolMock);
expect(evCacheProviderPoolMock.getMatching(anyObject(EvCacheOptions.class))).andReturn(evCacheProviderMock).anyTimes();
expect(httpResourceGroupMock.newRequestTemplate(anyObject(String.class))).andReturn(httpRequestTemplateMock).anyTimes();
expect(httpRequestTemplateMock.withMethod(anyObject(String.class))).andReturn(httpRequestTemplateMock).anyTimes();
expect(httpRequestTemplateMock.withUriTemplate(anyObject(String.class))).andReturn(httpRequestTemplateMock).anyTimes();
......@@ -147,6 +163,6 @@ public class MethodTemplateExecutorTest {
private MethodTemplateExecutor createExecutor(Class<?> clientInterface, String methodName) {
MethodTemplate methodTemplate = new MethodTemplate(methodByName(clientInterface, methodName));
return new MethodTemplateExecutor(httpResourceGroupMock, methodTemplate);
return new MethodTemplateExecutor(httpResourceGroupMock, methodTemplate, evCacheProviderPoolMock);
}
}
\ No newline at end of file
package com.netflix.ribbonclientextensions.typedclient;
import com.netflix.ribbonclientextensions.CacheProvider;
import com.netflix.ribbonclientextensions.evache.EvCacheOptions;
import com.netflix.ribbonclientextensions.typedclient.sample.EvCacheClasses.SampleEVCacheTranscoder;
import com.netflix.ribbonclientextensions.typedclient.sample.Movie;
import com.netflix.ribbonclientextensions.typedclient.sample.MovieServiceInterfaces.BrokenMovieService;
import com.netflix.ribbonclientextensions.typedclient.sample.MovieServiceInterfaces.HystrixOptionalAnnotationValues;
......@@ -37,6 +39,14 @@ public class MethodTemplateTest {
CacheProvider cacheProvider = template.getCacheProviders().get("findMovieById_{id}");
assertNotNull(cacheProvider);
assertTrue(cacheProvider instanceof SampleCacheProvider);
EvCacheOptions evOpts = template.getEvCacheOptions();
assertNotNull(evOpts);
assertEquals("movie-cache", evOpts.getCacheName());
assertEquals("movieService", evOpts.getAppName());
assertEquals("movie-{id}", evOpts.getCacheKeyTemplate());
assertEquals(50, evOpts.getTimeToLive());
assertTrue(evOpts.getTranscoder() instanceof SampleEVCacheTranscoder);
}
@Test
......
/*
* Copyright 2014 Netflix, Inc.
*
* 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 com.netflix.ribbonclientextensions.typedclient.sample;
import com.netflix.evcache.EVCacheTranscoder;
import net.spy.memcached.CachedData;
/**
* @author Tomasz Bak
*/
public class EvCacheClasses {
public static class SampleEVCacheTranscoder implements EVCacheTranscoder<Object> {
@Override
public boolean asyncDecode(CachedData d) {
return false;
}
@Override
public CachedData encode(Object o) {
return null;
}
@Override
public Object decode(CachedData d) {
return null;
}
@Override
public int getMaxSize() {
return 0;
}
}
}
......@@ -5,6 +5,7 @@ import com.netflix.ribbonclientextensions.typedclient.annotation.CacheProviders;
import com.netflix.ribbonclientextensions.typedclient.annotation.CacheProviders.Provider;
import com.netflix.ribbonclientextensions.typedclient.annotation.Content;
import com.netflix.ribbonclientextensions.typedclient.annotation.ContentTransformerClass;
import com.netflix.ribbonclientextensions.typedclient.annotation.EvCache;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http.Header;
import com.netflix.ribbonclientextensions.typedclient.annotation.Http.HttpMethod;
......@@ -12,6 +13,7 @@ import com.netflix.ribbonclientextensions.typedclient.annotation.Hystrix;
import com.netflix.ribbonclientextensions.typedclient.annotation.ResourceGroupSpec;
import com.netflix.ribbonclientextensions.typedclient.annotation.TemplateName;
import com.netflix.ribbonclientextensions.typedclient.annotation.Var;
import com.netflix.ribbonclientextensions.typedclient.sample.EvCacheClasses.SampleEVCacheTranscoder;
import com.netflix.ribbonclientextensions.typedclient.sample.HystrixHandlers.MovieFallbackHandler;
import com.netflix.ribbonclientextensions.typedclient.sample.HystrixHandlers.SampleHttpResponseValidator;
import io.netty.buffer.ByteBuf;
......@@ -38,7 +40,9 @@ public class MovieServiceInterfaces {
cacheKey = "findMovieById/{id}",
validator = SampleHttpResponseValidator.class,
fallbackHandler = MovieFallbackHandler.class)
@CacheProviders({@Provider(key = "findMovieById_{id}", provider = SampleCacheProviderFactory.class)})
@CacheProviders(@Provider(key = "findMovieById_{id}", provider = SampleCacheProviderFactory.class))
@EvCache(name = "movie-cache", appName = "movieService", cacheKeyTemplate = "movie-{id}", ttl = 50,
enableZoneFallback = true, transcoder = SampleEVCacheTranscoder.class)
RibbonRequest<Movie> findMovieById(@Var("id") String id);
@TemplateName("findRawMovieById")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册