Skip to content

Commit 0eb3ed1

Browse files
javier-godoypaodb
authored andcommitted
fix: instrument legacy callables in Vaadin 14-24
1 parent 5c1457b commit 0eb3ed1

File tree

3 files changed

+62
-35
lines changed

3 files changed

+62
-35
lines changed

src/main/java/com/flowingcode/vaadin/jsonmigration/ClassInstrumentationUtil.java

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
import java.lang.reflect.Constructor;
3131
import java.lang.reflect.Method;
3232
import java.lang.reflect.Modifier;
33-
import java.util.ArrayList;
3433
import java.util.List;
3534
import java.util.Map;
3635
import java.util.WeakHashMap;
3736
import java.util.concurrent.ConcurrentHashMap;
38-
import lombok.experimental.UtilityClass;
37+
import java.util.stream.Collectors;
38+
import java.util.stream.Stream;
3939
import org.objectweb.asm.ClassWriter;
4040
import org.objectweb.asm.MethodVisitor;
4141
import org.objectweb.asm.Opcodes;
@@ -59,11 +59,15 @@
5959
*
6060
* @author Javier Godoy / Flowing Code
6161
*/
62-
@UtilityClass
6362
final class ClassInstrumentationUtil {
6463

65-
private static final Map<ClassLoader, InstrumentedClassLoader> classLoaderCache =
66-
new WeakHashMap<>();
64+
private final int version;
65+
66+
private static final Map<ClassLoader, InstrumentedClassLoader> classLoaderCache = new WeakHashMap<>();
67+
68+
ClassInstrumentationUtil(int version) {
69+
this.version = version;
70+
}
6771

6872
/**
6973
* Creates and returns an instance of a dynamically instrumented class that extends the specified
@@ -144,34 +148,54 @@ public <T extends Component> Class<? extends T> instrumentClass(Class<T> parent)
144148
}
145149
}
146150

147-
private static boolean needsInstrumentation(Class<?> parent) {
151+
private boolean needsInstrumentation(Class<?> parent) {
148152
return !getInstrumentableMethods(parent).isEmpty();
149153
}
150154

151-
private static List<Method> getInstrumentableMethods(Class<?> parent) {
152-
List<Method> methods = new ArrayList<>();
153-
for (Method method : parent.getDeclaredMethods()) {
154-
if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isPrivate(method.getModifiers())) {
155+
private boolean hasLegacyVaadin() {
156+
return version <= 24;
157+
}
158+
159+
private static Stream<Method> getDeclaredCallables(Class<?> parent) {
160+
return Stream.of(parent.getDeclaredMethods()).filter(method -> {
161+
int modifiers = method.getModifiers();
162+
if (!Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers)) {
155163
boolean isCallable = method.isAnnotationPresent(ClientCallable.class);
156164
boolean isLegacyCallable = method.isAnnotationPresent(LegacyClientCallable.class);
157-
if (isCallable || isLegacyCallable) {
158-
boolean hasJsonValueReturn = JsonValue.class.isAssignableFrom(method.getReturnType());
159-
boolean hasJsonValueParams = hasJsonValueParameters(method);
160-
161-
if (isCallable && hasJsonValueParams) {
162-
throw new IllegalArgumentException(String.format(
163-
"Instrumented method '%s' in class '%s' has JsonValue arguments and must be annotated with @%s instead of @ClientCallable",
164-
method.getName(), method.getDeclaringClass().getName(),
165-
LegacyClientCallable.class.getName()));
166-
} else if (isCallable && hasJsonValueReturn) {
167-
methods.add(method);
168-
} else if (isLegacyCallable) {
169-
methods.add(method);
170-
}
165+
return isCallable || isLegacyCallable;
166+
}
167+
return false;
168+
});
169+
}
170+
171+
private List<Method> getInstrumentableMethods(Class<?> parent) {
172+
return getDeclaredCallables(parent).filter(method -> {
173+
boolean isCallable = method.isAnnotationPresent(ClientCallable.class);
174+
boolean isLegacyCallable = method.isAnnotationPresent(LegacyClientCallable.class);
175+
176+
if (hasLegacyVaadin()) {
177+
return isLegacyCallable;
178+
}
179+
180+
if (isCallable || isLegacyCallable) {
181+
boolean hasJsonValueReturn = JsonValue.class.isAssignableFrom(method.getReturnType());
182+
boolean hasJsonValueParams = hasJsonValueParameters(method);
183+
184+
if (isCallable && hasJsonValueParams) {
185+
throw new IllegalArgumentException(String.format(
186+
"Instrumented method '%s' in class '%s' has JsonValue arguments and must be annotated with @%s instead of @ClientCallable",
187+
method.getName(), method.getDeclaringClass().getName(),
188+
LegacyClientCallable.class.getName()));
189+
} else if (isCallable && hasJsonValueReturn) {
190+
return true;
191+
} else if (isLegacyCallable) {
192+
return true;
171193
}
172194
}
173-
}
174-
return methods;
195+
196+
return false;
197+
198+
}).collect(Collectors.toList());
175199
}
176200

177201
private static boolean hasJsonValueParameters(Method method) {
@@ -185,8 +209,7 @@ private static boolean hasJsonValueParameters(Method method) {
185209

186210
private <T extends Component> Class<? extends T> createInstrumentedClass(Class<T> parent,
187211
String className) throws Exception {
188-
InstrumentedClassLoader classLoader =
189-
getOrCreateInstrumentedClassLoader(parent.getClassLoader());
212+
InstrumentedClassLoader classLoader = getOrCreateInstrumentedClassLoader(parent.getClassLoader());
190213
return classLoader.defineInstrumentedClass(className, parent).asSubclass(parent);
191214
}
192215

@@ -196,7 +219,7 @@ private InstrumentedClassLoader getOrCreateInstrumentedClassLoader(ClassLoader p
196219
}
197220
}
198221

199-
private static final class InstrumentedClassLoader extends ClassLoader {
222+
private final class InstrumentedClassLoader extends ClassLoader {
200223

201224
private final Map<Class<?>, Class<?>> instrumentedClassCache = new ConcurrentHashMap<>();
202225

@@ -244,8 +267,8 @@ private void generateClientCallableOverrides(ClassWriter cw, Class<?> parent,
244267
}
245268

246269
private void generateMethodOverride(ClassWriter cw, Method method, String internalParentName) {
247-
boolean hasJsonValueReturn = JsonValue.class.isAssignableFrom(method.getReturnType());
248-
boolean hasJsonValueParams = hasJsonValueParameters(method);
270+
boolean hasJsonValueReturn = !hasLegacyVaadin() && JsonValue.class.isAssignableFrom(method.getReturnType());
271+
boolean hasJsonValueParams = !hasLegacyVaadin() && hasJsonValueParameters(method);
249272

250273
String overrideDescriptor = getMethodDescriptor(method, hasJsonValueParams);
251274
String superDescriptor = getMethodDescriptor(method, false);
@@ -300,7 +323,7 @@ private void generateMethodOverride(ClassWriter cw, Method method, String intern
300323
false);
301324
}
302325

303-
// Return converted result or void
326+
// Return result or void
304327
if (method.getReturnType() == Void.TYPE) {
305328
mv.visitInsn(Opcodes.RETURN);
306329
} else {

src/main/java/com/flowingcode/vaadin/jsonmigration/JsonMigrationHelper25.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
@NoArgsConstructor
4343
class JsonMigrationHelper25 implements JsonMigrationHelper {
4444

45+
private static final ClassInstrumentationUtil instrumentation = new ClassInstrumentationUtil(25);
46+
4547
@Override
4648
public <T extends Component> Class<? extends T> instrumentClass(Class<T> clazz) {
47-
return ClassInstrumentationUtil.instrumentClass(clazz);
49+
return instrumentation.instrumentClass(clazz);
4850
}
4951

5052
@Override

src/main/java/com/flowingcode/vaadin/jsonmigration/LegacyJsonMigrationHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@
3131
@NoArgsConstructor
3232
class LegacyJsonMigrationHelper implements JsonMigrationHelper {
3333

34+
private static final ClassInstrumentationUtil instrumentation = new ClassInstrumentationUtil(24);
35+
3436
@Override
35-
public <T extends Component> Class<T> instrumentClass(Class<T> clazz) {
36-
return clazz;
37+
public <T extends Component> Class<? extends T> instrumentClass(Class<T> clazz) {
38+
return instrumentation.instrumentClass(clazz);
3739
}
3840

3941
@Override

0 commit comments

Comments
 (0)