Adopt new MemoizingProxy in SpringLiveHoverWatchdog
This commit is contained in:
@@ -27,6 +27,7 @@ public class LocalSpringBootAppCache {
|
||||
|
||||
private static final Duration EXPIRE_AFTER = Duration.ofMillis(500); //Limits rate at which we refresh list of apps
|
||||
private long nextRefreshAfter = Long.MIN_VALUE;
|
||||
private final MemoizingProxy.Builder<LocalSpringBootApp> memoizingProxyBuilder = MemoizingProxy.builder(LocalSpringBootApp.class, Duration.ofMillis(4500), VirtualMachineDescriptor.class);
|
||||
|
||||
private ImmutableMap<VirtualMachineDescriptor, SpringBootApp> apps = ImmutableMap.of();
|
||||
|
||||
@@ -46,7 +47,7 @@ public class LocalSpringBootAppCache {
|
||||
newAppsBuilder.put(vm, existingApp);
|
||||
} else {
|
||||
try {
|
||||
LocalSpringBootApp localApp = MemoizingProxy.create(LocalSpringBootApp.class, Duration.ofMillis(4500), new Class[] {VirtualMachineDescriptor.class}, vm);
|
||||
LocalSpringBootApp localApp = memoizingProxyBuilder.newInstance(vm);
|
||||
newAppsBuilder.put(vm, localApp);
|
||||
} catch (Exception e) {
|
||||
//Ignore problems attaching to a VM. We will try again on next polling loop, if vm still exists.
|
||||
@@ -63,4 +64,5 @@ public class LocalSpringBootAppCache {
|
||||
apps = newApps;
|
||||
nextRefreshAfter = System.currentTimeMillis() + EXPIRE_AFTER.toMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ import java.util.Properties;
|
||||
import org.springframework.ide.vscode.commons.util.MemoizingProxy;
|
||||
|
||||
public class RemoteSpringBootApp extends AbstractSpringBootApp {
|
||||
|
||||
private static MemoizingProxy.Builder<RemoteSpringBootApp> memoizingProxyBuilder = MemoizingProxy.builder(RemoteSpringBootApp.class, Duration.ofMillis(4900),
|
||||
String.class, String.class, String.class, String.class, boolean.class
|
||||
);
|
||||
|
||||
private final String jmxUrl;
|
||||
private final String host;
|
||||
@@ -27,8 +31,7 @@ public class RemoteSpringBootApp extends AbstractSpringBootApp {
|
||||
private boolean keepChecking;
|
||||
|
||||
public static SpringBootApp create(String jmxUrl, String host, String port, String urlScheme, boolean keepChecking) {
|
||||
return MemoizingProxy.create(RemoteSpringBootApp.class, Duration.ofMillis(4900), new Class[] {String.class, String.class, String.class, String.class, boolean.class},
|
||||
jmxUrl, host, port, urlScheme, keepChecking);
|
||||
return memoizingProxyBuilder.newInstance(jmxUrl, host, port, urlScheme, keepChecking);
|
||||
}
|
||||
|
||||
protected RemoteSpringBootApp(String jmxUrl, String host, String port, String urlScheme, boolean keepChecking) {
|
||||
|
||||
@@ -154,85 +154,89 @@ public class MemoizingProxy {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Builder<T> builder(Class<T> klass, Duration duration, Class<?>... argTypes) throws Exception {
|
||||
if (klass.isInterface()) {
|
||||
Assert.isLegal(argTypes.length==0, "Should not provide constructor argument types for interface type "+klass.getSimpleName());
|
||||
DynamicType.Builder<T> builder = new ByteBuddy()
|
||||
.subclass(klass, ConstructorStrategy.Default.NO_CONSTRUCTORS)
|
||||
.defineField(F_DURATION, long.class, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC).value(duration.toMillis())
|
||||
.defineField(F_CACHE, Cache.class, Opcodes.ACC_PRIVATE)
|
||||
.defineField(F_DELEGATE, klass, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL)
|
||||
.method(ElementMatchers.isAbstract()).intercept(MethodDelegation.to(DelegateMethodInterceptor.class))
|
||||
.defineConstructor(Visibility.PUBLIC).withParameters(klass).intercept(
|
||||
MethodCall.invoke(Object.class.getConstructor())
|
||||
.andThen(FieldAccessor.ofField(F_DELEGATE).setsArgumentAt(0))
|
||||
.andThen(METHOD_DELEGATION.to(ClassConstructorInterceptor.class))
|
||||
);
|
||||
|
||||
Constructor<? extends T> constructor = builder.make().load(klass.getClassLoader()).getLoaded().getConstructor(klass);
|
||||
return new Builder<T>() {
|
||||
|
||||
@Override
|
||||
public T newInstance(Object... args) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't use 'newInstance' because '"+klass.getSimpleName()+" is an interface.\n"+
|
||||
"Use 'delegateTo' instead."
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T delegateTo(T delegate) {
|
||||
try {
|
||||
return constructor.newInstance(delegate);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
DynamicType.Builder<T> builder = new ByteBuddy()
|
||||
.subclass(klass, ConstructorStrategy.Default.NO_CONSTRUCTORS)
|
||||
.defineField(F_DURATION, long.class, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC).value(duration.toMillis())
|
||||
.defineField(F_CACHE, Cache.class, Opcodes.ACC_PRIVATE)
|
||||
.method(CACHABLE_METHODS).intercept(MethodDelegation.to(SuperMethodInterceptor.class))
|
||||
.defineConstructor(Visibility.PUBLIC).withParameters(argTypes).intercept(
|
||||
MethodCall.invoke(klass.getConstructor(argTypes)).withAllArguments()
|
||||
.andThen(METHOD_DELEGATION.to(ClassConstructorInterceptor.class))
|
||||
);
|
||||
|
||||
Constructor<? extends T> constructor = builder.make().load(klass.getClassLoader()).getLoaded().getConstructor(argTypes);
|
||||
return new Builder<T>() {
|
||||
@Override
|
||||
public T newInstance(Object... args) {
|
||||
try {
|
||||
return constructor.newInstance(args);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T delegateTo(T delegate) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't use 'delegateTo' because '"+klass.getSimpleName()+" is not an interface.\n"+
|
||||
"Use 'newInstance' instead."
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated: use the 'builder' method instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> T create(Class<T> klass, Duration duration, Class<?>[] argTypes, Object... args) {
|
||||
public static <T> Builder<T> builder(Class<T> klass, Duration duration, Class<?>... argTypes) {
|
||||
try {
|
||||
return builder(klass, duration, argTypes).newInstance(args);
|
||||
if (klass.isInterface()) {
|
||||
Assert.isLegal(argTypes.length==0, "Should not provide constructor argument types for interface type "+klass.getSimpleName());
|
||||
DynamicType.Builder<T> builder = new ByteBuddy()
|
||||
.subclass(klass, ConstructorStrategy.Default.NO_CONSTRUCTORS)
|
||||
.defineField(F_DURATION, long.class, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC).value(duration.toMillis())
|
||||
.defineField(F_CACHE, Cache.class, Opcodes.ACC_PRIVATE)
|
||||
.defineField(F_DELEGATE, klass, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL)
|
||||
.method(ElementMatchers.isAbstract()).intercept(MethodDelegation.to(DelegateMethodInterceptor.class))
|
||||
.defineConstructor(Visibility.PUBLIC).withParameters(klass).intercept(
|
||||
MethodCall.invoke(Object.class.getConstructor())
|
||||
.andThen(FieldAccessor.ofField(F_DELEGATE).setsArgumentAt(0))
|
||||
.andThen(METHOD_DELEGATION.to(ClassConstructorInterceptor.class))
|
||||
);
|
||||
|
||||
Constructor<? extends T> constructor = builder.make().load(klass.getClassLoader()).getLoaded().getConstructor(klass);
|
||||
return new Builder<T>() {
|
||||
|
||||
@Override
|
||||
public T newInstance(Object... args) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't use 'newInstance' because '"+klass.getSimpleName()+" is an interface.\n"+
|
||||
"Use 'delegateTo' instead."
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T delegateTo(T delegate) {
|
||||
try {
|
||||
return constructor.newInstance(delegate);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
DynamicType.Builder<T> builder = new ByteBuddy()
|
||||
.subclass(klass, ConstructorStrategy.Default.NO_CONSTRUCTORS)
|
||||
.defineField(F_DURATION, long.class, Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC).value(duration.toMillis())
|
||||
.defineField(F_CACHE, Cache.class, Opcodes.ACC_PRIVATE)
|
||||
.method(CACHABLE_METHODS).intercept(MethodDelegation.to(SuperMethodInterceptor.class))
|
||||
.defineConstructor(Visibility.PUBLIC).withParameters(argTypes).intercept(
|
||||
MethodCall.invoke(klass.getConstructor(argTypes)).withAllArguments()
|
||||
.andThen(METHOD_DELEGATION.to(ClassConstructorInterceptor.class))
|
||||
);
|
||||
|
||||
Constructor<? extends T> constructor = builder.make().load(klass.getClassLoader()).getLoaded().getConstructor(argTypes);
|
||||
return new Builder<T>() {
|
||||
@Override
|
||||
public T newInstance(Object... args) {
|
||||
try {
|
||||
return constructor.newInstance(args);
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T delegateTo(T delegate) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Can't use 'delegateTo' because '"+klass.getSimpleName()+" is not an interface.\n"+
|
||||
"Use 'newInstance' instead."
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw ExceptionUtil.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Deprecated: use the 'builder' method instead
|
||||
// */
|
||||
// @Deprecated
|
||||
// public static <T> T create(Class<T> klass, Duration duration, Class<?>[] argTypes, Object... args) {
|
||||
// try {
|
||||
// return builder(klass, duration, argTypes).newInstance(args);
|
||||
// } catch (Exception e) {
|
||||
// throw ExceptionUtil.unchecked(e);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ public class MemoizingProxyClassTest {
|
||||
}
|
||||
}
|
||||
|
||||
private TestSubject defaultTestSubject() {
|
||||
return MemoizingProxy.create(TestSubject.class, Duration.ofMinutes(1), CONSTRUCTOR_ARG_TYPES, "Johny", 45);
|
||||
private TestSubject defaultTestSubject() throws Exception {
|
||||
return MemoizingProxy.builder(TestSubject.class, Duration.ofMinutes(1), CONSTRUCTOR_ARG_TYPES).newInstance("Johny", 45);
|
||||
}
|
||||
|
||||
private void assertInvocations(String...expectedInvocations) {
|
||||
@@ -148,7 +148,7 @@ public class MemoizingProxyClassTest {
|
||||
}
|
||||
|
||||
@Test public void cacheExpires() throws Exception {
|
||||
this.proxy = MemoizingProxy.create(TestSubject.class, Duration.ofMillis(10), CONSTRUCTOR_ARG_TYPES, "Johny", 45);
|
||||
this.proxy = MemoizingProxy.builder(TestSubject.class, Duration.ofMillis(10), CONSTRUCTOR_ARG_TYPES).newInstance("Johny", 45);
|
||||
assertEquals("Johny", proxy.getMyName());
|
||||
assertInvocations("getMyName", "getName");
|
||||
sleep(20);
|
||||
@@ -157,15 +157,10 @@ public class MemoizingProxyClassTest {
|
||||
}
|
||||
|
||||
@Test public void proxyClassReused() throws Exception {
|
||||
//The old (deprecated) way creates a new proxy class for every proxy:
|
||||
TestSubject proxy1 = MemoizingProxy.create(TestSubject.class, Duration.ofMinutes(1), CONSTRUCTOR_ARG_TYPES, "Johny", 45);
|
||||
TestSubject proxy2 = MemoizingProxy.create(TestSubject.class, Duration.ofMinutes(1), CONSTRUCTOR_ARG_TYPES, "Johny", 45);
|
||||
assertFalse(proxy1.getClass().equals(proxy2.getClass()));
|
||||
|
||||
//Using the new 'builder' api allows re-using the same class (if used properly)
|
||||
Builder<TestSubject> builder = MemoizingProxy.builder(TestSubject.class, Duration.ofMinutes(1), CONSTRUCTOR_ARG_TYPES);
|
||||
proxy1 = builder.newInstance("Freddy", 12);
|
||||
proxy2 = builder.newInstance("Johny", 45);
|
||||
TestSubject proxy1 = builder.newInstance("Freddy", 12);
|
||||
TestSubject proxy2 = builder.newInstance("Johny", 45);
|
||||
assertFalse(proxy1.equals(proxy2)); // different instance...
|
||||
assertEquals(proxy1.getClass(), proxy2.getClass()); //same class
|
||||
}
|
||||
|
||||
@@ -38,9 +38,6 @@ import org.springframework.ide.vscode.commons.protocol.HighlightParams;
|
||||
import org.springframework.ide.vscode.commons.util.MemoizingProxy;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@@ -60,6 +57,7 @@ public class SpringLiveHoverWatchdog {
|
||||
private ScheduledThreadPoolExecutor timer;
|
||||
private JavaProjectFinder projectFinder;
|
||||
private final Map<String, AtomicReference<IJavaProject>> watchedDocs;
|
||||
|
||||
|
||||
|
||||
public SpringLiveHoverWatchdog(
|
||||
@@ -208,18 +206,17 @@ public class SpringLiveHoverWatchdog {
|
||||
}
|
||||
}
|
||||
|
||||
private final MemoizingProxy.Builder<SpringBootApp> memoizingProxyBuilder = MemoizingProxy.builder(SpringBootApp.class, Duration.ofMillis(20000));
|
||||
|
||||
private Collection<SpringBootApp> createAppCaches(Collection<SpringBootApp> runningBootApps) {
|
||||
return runningBootApps.stream().map(app -> {
|
||||
MethodInterceptor handler = new MemoizingProxy.MemoizingProxyHandler(app, Duration.ofMillis(20000));
|
||||
SpringBootApp proxied = (SpringBootApp) Enhancer.create(SpringBootApp.class, handler);
|
||||
|
||||
SpringBootApp proxied = memoizingProxyBuilder.delegateTo(app);
|
||||
try {
|
||||
proxied.getProcessName();
|
||||
proxied.getProcessID();
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
|
||||
return proxied;
|
||||
}).filter(app -> app != null).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user