Improve CachingModelProvider to wrap a cache around the models it provides
This commit is contained in:
@@ -108,9 +108,9 @@ public class BoshDeploymentManifestSchema implements YamlSchema {
|
||||
) {
|
||||
this.asts = asts;
|
||||
this.astTypes = astTypes;
|
||||
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider);
|
||||
this.stemcellsProvider = new CachingModelProvider<>(stemcellsProvider);
|
||||
this.releasesProvider = new CachingModelProvider<>(releasesProvider);
|
||||
this.cloudConfigProvider = new CachingModelProvider<>(cloudConfigProvider, CloudConfigModel.class);
|
||||
this.stemcellsProvider = new CachingModelProvider<>(stemcellsProvider, StemcellsModel.class);
|
||||
this.releasesProvider = new CachingModelProvider<>(releasesProvider, ReleasesModel.class);
|
||||
TYPE_UTIL = f.TYPE_UTIL;
|
||||
|
||||
V2_TOPLEVEL_TYPE = createV2Schema();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.bosh.models;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
@@ -35,9 +36,11 @@ public class CachingModelProvider<T> implements DynamicModelProvider<T> {
|
||||
private Cache<Object, CompletableFuture<T>> cache = createCache();
|
||||
|
||||
private final DynamicModelProvider<T> delegate;
|
||||
private Class<T> modelInterface;
|
||||
|
||||
public CachingModelProvider(DynamicModelProvider<T> delegate) {
|
||||
public CachingModelProvider(DynamicModelProvider<T> delegate, Class<T> modelInterface) {
|
||||
this.delegate = delegate;
|
||||
this.modelInterface = modelInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,15 +75,37 @@ public class CachingModelProvider<T> implements DynamicModelProvider<T> {
|
||||
synchronized (this) {
|
||||
cached = cache.get(key, () -> {
|
||||
try {
|
||||
return CompletableFuture.completedFuture(delegate.getModel(dc));
|
||||
return CompletableFuture.completedFuture(wrapWithCachingProxy(delegate.getModel(dc)));
|
||||
} catch (Throwable e) {
|
||||
CompletableFuture<T> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(e);
|
||||
return failed;
|
||||
return failed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
return cached.get();
|
||||
}
|
||||
|
||||
private static <T> CompletableFuture<T> failed(Throwable e) {
|
||||
CompletableFuture<T> failed = new CompletableFuture<>();
|
||||
failed.completeExceptionally(e);
|
||||
return failed;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T wrapWithCachingProxy(T model) {
|
||||
Cache<String, CompletableFuture<Object>> attributesCache = CacheBuilder.newBuilder().build();
|
||||
return (T) Proxy.newProxyInstance(modelInterface.getClassLoader(), new Class[] {modelInterface}, (o, m, a) -> {
|
||||
//We only support caching results for methods that have no arguments (for now, its all we need).
|
||||
if (m.getParameterTypes().length==0) {
|
||||
return attributesCache.get(m.getName(), () -> {
|
||||
try {
|
||||
return CompletableFuture.completedFuture((T)m.invoke(model, a));
|
||||
} catch (Throwable e) {
|
||||
return failed(e);
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
return m.invoke(model, a);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,9 +12,12 @@ package org.springframework.ide.vscode.bosh.models;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -23,24 +26,31 @@ import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CachingModelProviderTest {
|
||||
|
||||
interface BoxModel {
|
||||
String getContents();
|
||||
}
|
||||
|
||||
@Test public void goodValuesAreCached() throws Exception {
|
||||
DynamicModelProvider<String> modelProvider = mock(DynamicModelProvider.class);
|
||||
when(modelProvider.getModel(any())).thenReturn("RESULT");
|
||||
DynamicModelProvider<BoxModel> modelProvider = mock(DynamicModelProvider.class);
|
||||
BoxModel model = mock(BoxModel.class);
|
||||
when(modelProvider.getModel(any())).thenReturn(model);
|
||||
when(model.getContents()).thenReturn("RESULT");
|
||||
|
||||
DynamicModelProvider<String> cached = new CachingModelProvider<>(modelProvider);
|
||||
DynamicModelProvider<BoxModel> cached = new CachingModelProvider<>(modelProvider, BoxModel.class);
|
||||
|
||||
assertEquals("RESULT", cached.getModel(null));
|
||||
assertEquals("RESULT", cached.getModel(null));
|
||||
assertEquals("RESULT", cached.getModel(null));
|
||||
assertEquals("RESULT", cached.getModel(null).getContents());
|
||||
assertEquals("RESULT", cached.getModel(null).getContents());
|
||||
assertEquals("RESULT", cached.getModel(null).getContents());
|
||||
|
||||
verify(modelProvider, times(1)).getModel(any());
|
||||
verify(model, times(1)).getContents(); //model itself is also wrapped in a cache!
|
||||
}
|
||||
|
||||
@Test public void timeoutExceptionsAreCached() throws Exception {
|
||||
DynamicModelProvider<String> modelProvider = mock(DynamicModelProvider.class);
|
||||
when(modelProvider.getModel(any())).thenThrow(new TimeoutException("timed out"));
|
||||
|
||||
DynamicModelProvider<String> cached = new CachingModelProvider<>(modelProvider);
|
||||
DynamicModelProvider<String> cached = new CachingModelProvider<>(modelProvider, String.class);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
cached.getModel(null);
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SimpleDefinitionFinder<T extends SimpleLanguageServer> implements D
|
||||
* currently pointed at in the current document using String.indexOf.
|
||||
*/
|
||||
protected Flux<Location> findDefinitions(TextDocumentPositionParams params) {
|
||||
try {
|
||||
try {
|
||||
TextDocument doc = server.getTextDocumentService().get(params);
|
||||
if (doc != null) {
|
||||
int offset = doc.toOffset(params.getPosition());
|
||||
|
||||
Reference in New Issue
Block a user