Skip to content

Commit 46c6dc3

Browse files
committed
Don't create new JinjavaBeanELResolver instances per Jinjava or
JinjavaConfig for bean cache performance
1 parent 3c12c94 commit 46c6dc3

3 files changed

Lines changed: 106 additions & 72 deletions

File tree

src/main/java/com/hubspot/jinjava/JinjavaConfig.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,11 +180,9 @@ default AllowlistReturnTypeValidator getReturnTypeValidator() {
180180

181181
@Value.Default
182182
default ELResolver getElResolver() {
183-
return JinjavaInterpreterResolver.createDefaultResolver(
184-
isDefaultReadOnlyResolver(),
185-
getMethodValidator(),
186-
getReturnTypeValidator()
187-
);
183+
return isDefaultReadOnlyResolver()
184+
? JinjavaInterpreterResolver.DEFAULT_RESOLVER_READ_ONLY
185+
: JinjavaInterpreterResolver.DEFAULT_RESOLVER_READ_WRITE;
188186
}
189187

190188
@Value.Default

src/main/java/com/hubspot/jinjava/el/JinjavaInterpreterResolver.java

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44

55
import com.google.common.collect.ImmutableMap;
66
import com.hubspot.jinjava.el.ext.AbstractCallableMethod;
7-
import com.hubspot.jinjava.el.ext.AllowlistMethodValidator;
8-
import com.hubspot.jinjava.el.ext.AllowlistReturnTypeValidator;
97
import com.hubspot.jinjava.el.ext.DeferredParsingException;
108
import com.hubspot.jinjava.el.ext.ExtendedParser;
119
import com.hubspot.jinjava.el.ext.JinjavaBeanELResolver;
1210
import com.hubspot.jinjava.el.ext.JinjavaListELResolver;
13-
import com.hubspot.jinjava.el.ext.MethodValidatorConfig;
1411
import com.hubspot.jinjava.el.ext.NamedParameter;
15-
import com.hubspot.jinjava.el.ext.ReturnTypeValidatorConfig;
1612
import com.hubspot.jinjava.interpret.DeferredValueException;
1713
import com.hubspot.jinjava.interpret.DisabledException;
1814
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
@@ -56,39 +52,25 @@
5652

5753
public class JinjavaInterpreterResolver extends SimpleResolver {
5854

59-
public static ELResolver createDefaultResolver(
60-
boolean readOnly,
61-
AllowlistMethodValidator allowlistMethodValidator,
62-
AllowlistReturnTypeValidator allowlistReturnTypeValidator
63-
) {
64-
return new CompositeELResolver() {
65-
{
66-
add(new ArrayELResolver(readOnly));
67-
add(new JinjavaListELResolver(readOnly));
68-
add(new TypeConvertingMapELResolver(readOnly));
69-
add(new ResourceBundleELResolver());
70-
add(
71-
new JinjavaBeanELResolver(
72-
readOnly,
73-
allowlistMethodValidator,
74-
allowlistReturnTypeValidator
75-
)
76-
);
77-
}
78-
};
79-
}
80-
81-
public static final ELResolver DEFAULT_RESOLVER_READ_ONLY = createDefaultResolver(
82-
true,
83-
AllowlistMethodValidator.create(MethodValidatorConfig.of()),
84-
AllowlistReturnTypeValidator.create(ReturnTypeValidatorConfig.of())
85-
);
86-
87-
public static final ELResolver DEFAULT_RESOLVER_READ_WRITE = createDefaultResolver(
88-
false,
89-
AllowlistMethodValidator.create(MethodValidatorConfig.of()),
90-
AllowlistReturnTypeValidator.create(ReturnTypeValidatorConfig.of())
91-
);
55+
public static final ELResolver DEFAULT_RESOLVER_READ_ONLY = new CompositeELResolver() {
56+
{
57+
add(new ArrayELResolver(true));
58+
add(new JinjavaListELResolver(true));
59+
add(new TypeConvertingMapELResolver(true));
60+
add(new ResourceBundleELResolver());
61+
add(new JinjavaBeanELResolver(true));
62+
}
63+
};
64+
65+
public static final ELResolver DEFAULT_RESOLVER_READ_WRITE = new CompositeELResolver() {
66+
{
67+
add(new ArrayELResolver(false));
68+
add(new JinjavaListELResolver(false));
69+
add(new TypeConvertingMapELResolver(false));
70+
add(new ResourceBundleELResolver());
71+
add(new JinjavaBeanELResolver(false));
72+
}
73+
};
9274

9375
private final JinjavaInterpreter interpreter;
9476
private final ObjectUnwrapper objectUnwrapper;

src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java

Lines changed: 84 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,24 @@
22

33
import com.google.common.base.CaseFormat;
44
import com.google.common.collect.ImmutableSet;
5+
import com.hubspot.jinjava.JinjavaConfig;
56
import com.hubspot.jinjava.interpret.DeferredValueException;
7+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
68
import com.hubspot.jinjava.util.EagerReconstructionUtils;
9+
import java.beans.IntrospectionException;
10+
import java.beans.Introspector;
11+
import java.beans.MethodDescriptor;
712
import java.lang.invoke.MethodType;
813
import java.lang.reflect.Method;
14+
import java.util.HashMap;
915
import java.util.LinkedList;
1016
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Objects;
1119
import java.util.Set;
20+
import java.util.concurrent.ConcurrentHashMap;
1221
import javax.el.ELContext;
22+
import javax.el.ELException;
1323
import javax.el.MethodNotFoundException;
1424

1525
/**
@@ -39,35 +49,18 @@ public class JinjavaBeanELResolver extends BeanELResolver {
3949
.add("merge")
4050
.build();
4151

42-
private final AllowlistMethodValidator allowlistMethodValidator;
43-
private final AllowlistReturnTypeValidator allowlistReturnTypeValidator;
52+
private final ConcurrentHashMap<Class<?>, BeanMethods> beanMethodsCache;
4453

4554
public JinjavaBeanELResolver() {
46-
this(
47-
true,
48-
AllowlistMethodValidator.create(MethodValidatorConfig.of()),
49-
AllowlistReturnTypeValidator.create(ReturnTypeValidatorConfig.of())
50-
);
51-
}
52-
53-
public JinjavaBeanELResolver(
54-
AllowlistMethodValidator allowlistMethodValidator,
55-
AllowlistReturnTypeValidator allowlistReturnTypeValidator
56-
) {
57-
this(true, allowlistMethodValidator, allowlistReturnTypeValidator);
55+
this(true);
5856
}
5957

6058
/**
6159
* Creates a new read/write {@link JinjavaBeanELResolver}.
6260
*/
63-
public JinjavaBeanELResolver(
64-
boolean readOnly,
65-
AllowlistMethodValidator allowlistMethodValidator,
66-
AllowlistReturnTypeValidator allowlistReturnTypeValidator
67-
) {
61+
public JinjavaBeanELResolver(boolean readOnly) {
6862
super(readOnly);
69-
this.allowlistMethodValidator = allowlistMethodValidator;
70-
this.allowlistReturnTypeValidator = allowlistReturnTypeValidator;
63+
this.beanMethodsCache = new ConcurrentHashMap<Class<?>, BeanMethods>();
7164
}
7265

7366
@Override
@@ -77,9 +70,9 @@ public Class<?> getType(ELContext context, Object base, Object property) {
7770

7871
@Override
7972
public Object getValue(ELContext context, Object base, Object property) {
80-
return allowlistReturnTypeValidator.validateReturnType(
81-
super.getValue(context, base, transformPropertyName(property))
82-
);
73+
return getJinjavaConfig()
74+
.getReturnTypeValidator()
75+
.validateReturnType(super.getValue(context, base, transformPropertyName(property)));
8376
}
8477

8578
@Override
@@ -118,9 +111,9 @@ public Object invoke(
118111
);
119112
}
120113

121-
return allowlistReturnTypeValidator.validateReturnType(
122-
super.invoke(context, base, method, paramTypes, params)
123-
);
114+
return getJinjavaConfig()
115+
.getReturnTypeValidator()
116+
.validateReturnType(super.invoke(context, base, method, paramTypes, params));
124117
}
125118

126119
@Override
@@ -167,17 +160,30 @@ protected Method findMethod(
167160
)
168161
);
169162
}
170-
return allowlistMethodValidator.validateMethod(method);
163+
return getJinjavaConfig().getMethodValidator().validateMethod(method);
171164
}
172165

173166
@Override
174167
protected Method getWriteMethod(Object base, Object property) {
175-
return allowlistMethodValidator.validateMethod(super.getWriteMethod(base, property));
168+
return getJinjavaConfig()
169+
.getMethodValidator()
170+
.validateMethod(super.getWriteMethod(base, property));
176171
}
177172

178173
@Override
179174
protected Method getReadMethod(Object base, Object property) {
180-
return allowlistMethodValidator.validateMethod(super.getReadMethod(base, property));
175+
return getJinjavaConfig()
176+
.getMethodValidator()
177+
.validateMethod(super.getReadMethod(base, property));
178+
}
179+
180+
private static JinjavaConfig getJinjavaConfig() {
181+
return Objects
182+
.requireNonNull(
183+
JinjavaInterpreter.getCurrent(),
184+
"JinjavaInterpreter.closeablePushCurrent must be used if using a JinjavaInterpreter directly"
185+
)
186+
.getConfig();
181187
}
182188

183189
private static boolean checkAssignableParameterTypes(Object[] params, Method method) {
@@ -221,4 +227,52 @@ private String transformPropertyName(Object property) {
221227
}
222228
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, propertyStr);
223229
}
230+
231+
protected final class BeanMethods {
232+
233+
private final Map<String, List<BeanMethod>> map = new HashMap<>();
234+
235+
public BeanMethods(Class<?> baseClass) {
236+
MethodDescriptor[] descriptors;
237+
try {
238+
descriptors = Introspector.getBeanInfo(baseClass).getMethodDescriptors();
239+
} catch (IntrospectionException e) {
240+
throw new ELException(e);
241+
}
242+
for (MethodDescriptor descriptor : descriptors) {
243+
map.compute(
244+
descriptor.getName(),
245+
(k, v) -> {
246+
if (v == null) {
247+
v = new LinkedList<>();
248+
}
249+
v.add(new BeanMethod(descriptor));
250+
return v;
251+
}
252+
);
253+
}
254+
}
255+
256+
public List<BeanMethod> getBeanMethods(String methodName) {
257+
return map.get(methodName);
258+
}
259+
}
260+
261+
protected final class BeanMethod {
262+
263+
private final MethodDescriptor descriptor;
264+
265+
private Method method;
266+
267+
public BeanMethod(MethodDescriptor descriptor) {
268+
this.descriptor = descriptor;
269+
}
270+
271+
public Method getMethod() {
272+
if (method == null) {
273+
method = findAccessibleMethod(descriptor.getMethod());
274+
}
275+
return method;
276+
}
277+
}
224278
}

0 commit comments

Comments
 (0)