Fixed an NPE in ImplicitPropertyAccessor due to putting null value in ConcurrentHashMap
This commit is contained in:
parent
2c3d0f10d9
commit
410dcdada2
|
@ -27,8 +27,7 @@ import org.springframework.expression.ConstructorResolver;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.spel.support.ReflectiveConstructorResolver;
|
import org.springframework.expression.spel.support.ReflectiveConstructorResolver;
|
||||||
|
|
||||||
final class ImplicitConstructorResolver implements
|
final class ImplicitConstructorResolver implements ConstructorResolver {
|
||||||
ConstructorResolver {
|
|
||||||
|
|
||||||
private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver();
|
private final ReflectiveConstructorResolver delegate = new ReflectiveConstructorResolver();
|
||||||
|
|
||||||
|
|
|
@ -39,20 +39,21 @@ final class ImplicitMethodResolver implements MethodResolver {
|
||||||
private final ReflectiveMethodResolver delegate = new ReflectiveMethodResolver();
|
private final ReflectiveMethodResolver delegate = new ReflectiveMethodResolver();
|
||||||
|
|
||||||
private static final MethodExecutor NULL_ME = new MethodExecutor() {
|
private static final MethodExecutor NULL_ME = new MethodExecutor() {
|
||||||
|
@Override
|
||||||
public TypedValue execute(final EvaluationContext context, final Object target,
|
public TypedValue execute(final EvaluationContext context, final Object target,
|
||||||
final Object... arguments) throws AccessException {
|
final Object... arguments) throws AccessException {
|
||||||
return null;
|
throw new UnsupportedOperationException("This method should never be called");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final class ImplicitMethodExecutor implements
|
private static final class ImplicitMethodExecutor implements MethodExecutor {
|
||||||
MethodExecutor {
|
|
||||||
private final MethodExecutor executor;
|
private final MethodExecutor executor;
|
||||||
|
|
||||||
public ImplicitMethodExecutor(final MethodExecutor executor) {
|
public ImplicitMethodExecutor(final MethodExecutor executor) {
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TypedValue execute(final EvaluationContext context, final Object target,
|
public TypedValue execute(final EvaluationContext context, final Object target,
|
||||||
final Object... arguments) throws AccessException {
|
final Object... arguments) throws AccessException {
|
||||||
Object[] modifiedArguments = new Object[arguments.length + 1];
|
Object[] modifiedArguments = new Object[arguments.length + 1];
|
||||||
|
@ -66,7 +67,7 @@ final class ImplicitMethodResolver implements MethodResolver {
|
||||||
public MethodExecutor resolve(
|
public MethodExecutor resolve(
|
||||||
final EvaluationContext context, final Object targetObject,
|
final EvaluationContext context, final Object targetObject,
|
||||||
final String name, final List<TypeDescriptor> argumentTypes)
|
final String name, final List<TypeDescriptor> argumentTypes)
|
||||||
throws AccessException {
|
throws AccessException {
|
||||||
if (targetObject == null) {
|
if (targetObject == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,22 @@ final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor {
|
||||||
private static final ConcurrentHashMap<String, MethodExecutor> CACHE =
|
private static final ConcurrentHashMap<String, MethodExecutor> CACHE =
|
||||||
new ConcurrentHashMap<String, MethodExecutor>();
|
new ConcurrentHashMap<String, MethodExecutor>();
|
||||||
|
|
||||||
|
private static final MethodExecutor NULL_ME = new MethodExecutor() {
|
||||||
|
@Override
|
||||||
|
public TypedValue execute(final EvaluationContext context, final Object target,
|
||||||
|
final Object... arguments) throws AccessException {
|
||||||
|
throw new UnsupportedOperationException("This method should never be called");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean canRead(final EvaluationContext context,
|
public boolean canRead(final EvaluationContext context,
|
||||||
final Object target, final String name)
|
final Object target, final String name)
|
||||||
throws AccessException {
|
throws AccessException {
|
||||||
Assert.notNull(target, "target is null");
|
Assert.notNull(target, "target is null");
|
||||||
String cacheKey = target.getClass().getName() + "." + name;
|
String cacheKey = target.getClass().getName() + "." + name;
|
||||||
if (CACHE.containsKey(cacheKey)) {
|
if (CACHE.containsKey(cacheKey)) {
|
||||||
return CACHE.get(cacheKey) != null;
|
return CACHE.get(cacheKey) != NULL_ME;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (MethodResolver mr : context.getMethodResolvers()) {
|
for (MethodResolver mr : context.getMethodResolvers()) {
|
||||||
|
@ -52,10 +61,11 @@ final class ImplicitPropertyAccessor extends ReadOnlyGenericPropertyAccessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CACHE.putIfAbsent(cacheKey, null);
|
CACHE.putIfAbsent(cacheKey, NULL_ME);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public TypedValue read(final EvaluationContext context,
|
public TypedValue read(final EvaluationContext context,
|
||||||
final Object target, final String name)
|
final Object target, final String name)
|
||||||
throws AccessException {
|
throws AccessException {
|
||||||
|
|
|
@ -23,19 +23,21 @@ import org.springframework.expression.AccessException;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.PropertyAccessor;
|
import org.springframework.expression.PropertyAccessor;
|
||||||
|
|
||||||
abstract class ReadOnlyGenericPropertyAccessor implements
|
abstract class ReadOnlyGenericPropertyAccessor implements PropertyAccessor {
|
||||||
PropertyAccessor {
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public final boolean canWrite(final EvaluationContext context,
|
public final boolean canWrite(final EvaluationContext context,
|
||||||
final Object target, final String name) throws AccessException {
|
final Object target, final String name) throws AccessException {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public final Class[] getSpecificTargetClasses() {
|
public final Class[] getSpecificTargetClasses() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public final void write(final EvaluationContext context, final Object target,
|
public final void write(final EvaluationContext context, final Object target,
|
||||||
final String name, final Object newValue) throws AccessException {
|
final String name, final Object newValue) throws AccessException {
|
||||||
throw new AccessException(MessageFormat.format(
|
throw new AccessException(MessageFormat.format(
|
||||||
|
|
Loading…
Reference in New Issue