mirror of
https://github.com/apache/struts.git
synced 2026-09-06 14:19:39 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b039dc5079 | |||
| 6374e31384 | |||
| 803b5cbbd0 | |||
| 1dda92ed23 | |||
| cda38c79db | |||
| fbb904e9c6 | |||
| f7923fc704 | |||
| 47718f5a8a | |||
| e2e8fa1f33 | |||
| 71c204f04b |
@@ -18,10 +18,9 @@
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.conversion.NullHandler;
|
||||
|
||||
public class OgnlNullHandlerWrapper implements ognl.NullHandler {
|
||||
public class OgnlNullHandlerWrapper implements ognl.NullHandler<StrutsContext> {
|
||||
|
||||
private final NullHandler wrapped;
|
||||
|
||||
@@ -30,13 +29,13 @@ public class OgnlNullHandlerWrapper implements ognl.NullHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullMethodResult(OgnlContext context, Object target,
|
||||
public Object nullMethodResult(StrutsContext context, Object target,
|
||||
String methodName, Object[] args) {
|
||||
return wrapped.nullMethodResult(context, target, methodName, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullPropertyValue(OgnlContext context, Object target, Object property) {
|
||||
public Object nullPropertyValue(StrutsContext context, Object target, Object property) {
|
||||
return wrapped.nullPropertyValue(context, target, property);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import ognl.Ognl;
|
||||
public class OgnlReflectionContextFactory implements ReflectionContextFactory {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public OgnlContext createDefaultContext(Object root) {
|
||||
return Ognl.createDefaultContext(root);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.conversion.TypeConverter;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
@@ -26,7 +25,7 @@ import java.lang.reflect.Member;
|
||||
/**
|
||||
* Wraps an XWork type conversion class for as an OGNL TypeConverter
|
||||
*/
|
||||
public class OgnlTypeConverterWrapper implements ognl.TypeConverter {
|
||||
public class OgnlTypeConverterWrapper implements ognl.TypeConverter<StrutsContext> {
|
||||
|
||||
private final TypeConverter typeConverter;
|
||||
|
||||
@@ -38,7 +37,7 @@ public class OgnlTypeConverterWrapper implements ognl.TypeConverter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertValue(OgnlContext context, Object target, Member member, String propertyName, Object value, Class<?> toType) {
|
||||
public Object convertValue(StrutsContext context, Object target, Member member, String propertyName, Object value, Class<?> toType) {
|
||||
return typeConverter.convertValue(context, target, member, propertyName, value, toType);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class OgnlUtil {
|
||||
|
||||
private final OgnlCache<String, Object> expressionCache;
|
||||
private final OgnlCache<Class<?>, BeanInfo> beanInfoCache;
|
||||
private TypeConverter defaultConverter;
|
||||
private TypeConverter<StrutsContext> defaultConverter;
|
||||
private final OgnlGuard ognlGuard;
|
||||
|
||||
private boolean devMode;
|
||||
@@ -211,14 +211,14 @@ public class OgnlUtil {
|
||||
* @return an OgnlContext instance
|
||||
* @since 7.2.0
|
||||
*/
|
||||
private OgnlContext ensureOgnlContext(Map<String, Object> context) {
|
||||
if (context instanceof OgnlContext ognlContext) {
|
||||
return ognlContext;
|
||||
private StrutsContext ensureOgnlContext(Map<String, Object> context) {
|
||||
if (context instanceof StrutsContext strutsContext) {
|
||||
return strutsContext;
|
||||
}
|
||||
// Create a new OgnlContext and copy the Map contents
|
||||
OgnlContext ognlContext = createDefaultContext(null);
|
||||
ognlContext.putAll(context);
|
||||
return ognlContext;
|
||||
// Create a new StrutsContext and copy the Map contents
|
||||
StrutsContext strutsContext = createDefaultContext(null);
|
||||
strutsContext.putAll(context);
|
||||
return strutsContext;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,9 +247,9 @@ public class OgnlUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
OgnlContext ognlContext = ensureOgnlContext(context);
|
||||
StrutsContext strutsContext = ensureOgnlContext(context);
|
||||
try {
|
||||
withRoot(ognlContext, o, () -> {
|
||||
withRoot(strutsContext, o, () -> {
|
||||
for (Map.Entry<String, ?> entry : props.entrySet()) {
|
||||
String expression = entry.getKey();
|
||||
internalSetProperty(expression, entry.getValue(), o, context, throwPropertyExceptions);
|
||||
@@ -309,9 +309,9 @@ public class OgnlUtil {
|
||||
* problems setting the property
|
||||
*/
|
||||
public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
|
||||
OgnlContext ognlContext = ensureOgnlContext(context);
|
||||
StrutsContext strutsContext = ensureOgnlContext(context);
|
||||
try {
|
||||
withRoot(ognlContext, o, () -> internalSetProperty(name, value, o, context, throwPropertyExceptions));
|
||||
withRoot(strutsContext, o, () -> internalSetProperty(name, value, o, context, throwPropertyExceptions));
|
||||
} catch (OgnlException e) {
|
||||
// Should never happen as internalSetProperty catches OgnlException
|
||||
throw new IllegalStateException("Unexpected OgnlException in setProperty", e);
|
||||
@@ -424,7 +424,7 @@ public class OgnlUtil {
|
||||
for (TreeValidator validator : treeValidators) {
|
||||
validator.validate(tree, checkContext);
|
||||
}
|
||||
OgnlContext ognlContext = (OgnlContext) context;
|
||||
StrutsContext ognlContext = (StrutsContext) context;
|
||||
withRoot(ognlContext, root, () -> Ognl.setValue(tree, ognlContext, root, value));
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ public class OgnlUtil {
|
||||
for (TreeValidator validator : treeValidators) {
|
||||
validator.validate(tree, checkContext);
|
||||
}
|
||||
OgnlContext ognlContext = (OgnlContext) context;
|
||||
StrutsContext ognlContext = (StrutsContext) context;
|
||||
return withRoot(ognlContext, root, () -> (T) Ognl.getValue(tree, ognlContext, root, resultType));
|
||||
}
|
||||
|
||||
@@ -548,8 +548,8 @@ public class OgnlUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object> contextFrom = createDefaultContext(from);
|
||||
final Map<String, Object> contextTo = createDefaultContext(to);
|
||||
final StrutsContext contextFrom = createDefaultContext(from);
|
||||
final StrutsContext contextTo = createDefaultContext(to);
|
||||
|
||||
PropertyDescriptor[] fromPds;
|
||||
PropertyDescriptor[] toPds;
|
||||
@@ -654,7 +654,7 @@ public class OgnlUtil {
|
||||
*/
|
||||
public Map<String, Object> getBeanMap(final Object source) throws IntrospectionException, OgnlException {
|
||||
Map<String, Object> beanMap = new HashMap<>();
|
||||
final Map<String, Object> sourceMap = createDefaultContext(source);
|
||||
final StrutsContext sourceMap = createDefaultContext(source);
|
||||
PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source);
|
||||
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||
final String propertyName = propertyDescriptor.getDisplayName();
|
||||
@@ -724,18 +724,21 @@ public class OgnlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
protected OgnlContext createDefaultContext(Object root) {
|
||||
protected StrutsContext createDefaultContext(Object root) {
|
||||
return createDefaultContext(root, null);
|
||||
}
|
||||
|
||||
protected OgnlContext createDefaultContext(Object root, ClassResolver resolver) {
|
||||
protected StrutsContext createDefaultContext(Object root, ClassResolver<StrutsContext> resolver) {
|
||||
if (resolver == null) {
|
||||
resolver = container.getInstance(RootAccessor.class);
|
||||
if (resolver == null) {
|
||||
throw new IllegalStateException("Cannot find ClassResolver");
|
||||
}
|
||||
}
|
||||
return Ognl.createDefaultContext(root, container.getInstance(SecurityMemberAccess.class), resolver, defaultConverter);
|
||||
StrutsContext context = new StrutsContext(
|
||||
container.getInstance(SecurityMemberAccess.class), resolver, defaultConverter);
|
||||
context.withRoot(root);
|
||||
return context;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
@@ -762,13 +765,13 @@ public class OgnlUtil {
|
||||
* @param action the action to execute
|
||||
* @throws OgnlException if the action throws an OgnlException
|
||||
*/
|
||||
private void withRoot(OgnlContext context, Object root, OgnlAction action) throws OgnlException {
|
||||
Object oldRoot = Ognl.getRoot(context);
|
||||
private void withRoot(StrutsContext context, Object root, OgnlAction action) throws OgnlException {
|
||||
Object oldRoot = context.getRoot();
|
||||
try {
|
||||
Ognl.setRoot(context, root);
|
||||
context.withRoot(root);
|
||||
action.run();
|
||||
} finally {
|
||||
Ognl.setRoot(context, oldRoot);
|
||||
context.withRoot(oldRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,13 +786,13 @@ public class OgnlUtil {
|
||||
* @return the result of the supplier
|
||||
* @throws OgnlException if the supplier throws an OgnlException
|
||||
*/
|
||||
private <T> T withRoot(OgnlContext context, Object root, OgnlSupplier<T> supplier) throws OgnlException {
|
||||
Object oldRoot = Ognl.getRoot(context);
|
||||
private <T> T withRoot(StrutsContext context, Object root, OgnlSupplier<T> supplier) throws OgnlException {
|
||||
Object oldRoot = context.getRoot();
|
||||
try {
|
||||
Ognl.setRoot(context, root);
|
||||
context.withRoot(root);
|
||||
return supplier.get();
|
||||
} finally {
|
||||
Ognl.setRoot(context, oldRoot);
|
||||
context.withRoot(oldRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -68,7 +66,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
private static final String MAP_IDENTIFIER_KEY = "org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
|
||||
|
||||
protected CompoundRoot root;
|
||||
protected transient Map<String, Object> context;
|
||||
protected transient StrutsContext context;
|
||||
protected Class defaultType;
|
||||
protected Map<Object, Object> overrides;
|
||||
protected transient OgnlUtil ognlUtil;
|
||||
@@ -121,12 +119,12 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
protected void setRoot(XWorkConverter xworkConverter, RootAccessor accessor, CompoundRoot compoundRoot, SecurityMemberAccess securityMemberAccess) {
|
||||
this.root = compoundRoot;
|
||||
this.securityMemberAccess = securityMemberAccess;
|
||||
OgnlContext ognlContext = Ognl.createDefaultContext(this.root, securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
|
||||
this.context = ognlContext;
|
||||
this.context = new StrutsContext(securityMemberAccess, accessor, new OgnlTypeConverterWrapper(xworkConverter));
|
||||
this.context.withRoot(this.root);
|
||||
this.converter = xworkConverter;
|
||||
context.put(VALUE_STACK, this);
|
||||
ognlContext.setTraceEvaluations(false);
|
||||
ognlContext.setKeepLastEvaluation(false);
|
||||
context.setTraceEvaluations(false);
|
||||
context.setKeepLastEvaluation(false);
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
@@ -508,9 +506,7 @@ public class OgnlValueStack implements Serializable, ValueStack, ClearableValueS
|
||||
|
||||
@Override
|
||||
public void clearContextValues() {
|
||||
//this is an OGNL ValueStack so the context will be an OgnlContext
|
||||
//it would be better to make context of type OgnlContext
|
||||
((OgnlContext) context).getValues().clear();
|
||||
context.getValues().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -54,6 +54,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
}
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected void setCompoundRootAccessor(RootAccessor compoundRootAccessor) {
|
||||
this.compoundRootAccessor = compoundRootAccessor;
|
||||
OgnlRuntime.setPropertyAccessor(CompoundRoot.class, compoundRootAccessor);
|
||||
@@ -110,6 +111,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
* {@link #setMethodAccessor} and can be configured using the extension point
|
||||
* {@link StrutsConstants#STRUTS_METHOD_ACCESSOR}.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected void registerAdditionalMethodAccessors() {
|
||||
Set<String> names = container.getInstanceNames(MethodAccessor.class);
|
||||
for (String name : names) {
|
||||
@@ -145,6 +147,7 @@ public class OgnlValueStackFactory implements ValueStackFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected void registerPropertyAccessors() throws ClassNotFoundException {
|
||||
Set<String> names = container.getInstanceNames(PropertyAccessor.class);
|
||||
for (String name : names) {
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.MemberAccess;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -54,7 +53,7 @@ import static org.apache.struts2.util.DebugUtils.logWarningForFirstOccurrence;
|
||||
* Allows access decisions to be made on the basis of whether a member is static or not.
|
||||
* Also blocks or allows access to properties.
|
||||
*/
|
||||
public class SecurityMemberAccess implements MemberAccess {
|
||||
public class SecurityMemberAccess implements MemberAccess<StrutsContext> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
|
||||
|
||||
@@ -115,7 +114,7 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setup(OgnlContext context, Object target, Member member, String propertyName) {
|
||||
public Object setup(StrutsContext context, Object target, Member member, String propertyName) {
|
||||
Object result = null;
|
||||
|
||||
if (isAccessible(context, target, member, propertyName)) {
|
||||
@@ -130,7 +129,7 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(OgnlContext context, Object target, Member member, String propertyName, Object state) {
|
||||
public void restore(StrutsContext context, Object target, Member member, String propertyName, Object state) {
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +144,7 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(OgnlContext context, Object target, Member member, String propertyName) {
|
||||
public boolean isAccessible(StrutsContext context, Object target, Member member, String propertyName) {
|
||||
LOG.debug("Checking access for [target: {}, member: {}, property: {}]", target, member, propertyName);
|
||||
|
||||
if (member == null) {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.ClassResolver;
|
||||
import ognl.MemberAccess;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.TypeConverter;
|
||||
|
||||
/**
|
||||
* Struts-specific OGNL evaluation context. Extends {@link OgnlContext} with the
|
||||
* self-bounded generic parameter to enable type-safe access in all OGNL interface
|
||||
* implementations ({@link MemberAccess}, {@link ognl.PropertyAccessor}, etc.).
|
||||
*
|
||||
* <p>Phase 1: minimal subclass delegating to super constructors.
|
||||
* Future phases will promote stringly-typed map entries (e.g. {@code DENY_METHOD_EXECUTION},
|
||||
* {@code CREATE_NULL_OBJECTS}) to proper typed fields.</p>
|
||||
*
|
||||
* @since 7.2.0
|
||||
*/
|
||||
public class StrutsContext extends OgnlContext<StrutsContext> {
|
||||
|
||||
public StrutsContext(MemberAccess<StrutsContext> memberAccess) {
|
||||
super(memberAccess);
|
||||
}
|
||||
|
||||
public StrutsContext(MemberAccess<StrutsContext> memberAccess,
|
||||
ClassResolver<StrutsContext> classResolver) {
|
||||
super(memberAccess, classResolver);
|
||||
}
|
||||
|
||||
public StrutsContext(MemberAccess<StrutsContext> memberAccess,
|
||||
ClassResolver<StrutsContext> classResolver,
|
||||
TypeConverter<StrutsContext> typeConverter) {
|
||||
super(memberAccess, classResolver, typeConverter);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.conversion.TypeConverter;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
@@ -29,19 +28,19 @@ import java.util.Map;
|
||||
*/
|
||||
public class XWorkTypeConverterWrapper implements TypeConverter {
|
||||
|
||||
private final ognl.TypeConverter typeConverter;
|
||||
private final ognl.TypeConverter<StrutsContext> typeConverter;
|
||||
|
||||
public XWorkTypeConverterWrapper(ognl.TypeConverter conv) {
|
||||
public XWorkTypeConverterWrapper(ognl.TypeConverter<StrutsContext> conv) {
|
||||
this.typeConverter = conv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) {
|
||||
// Cast context to OgnlContext for OGNL 3.4.8+ compatibility
|
||||
OgnlContext ognlContext = (context instanceof OgnlContext oc) ? oc : null;
|
||||
if (ognlContext == null) {
|
||||
throw new IllegalArgumentException("Context must be an OgnlContext for OGNL 3.4.8+");
|
||||
// Cast context to StrutsContext for OGNL 3.5.x compatibility
|
||||
StrutsContext strutsContext = (context instanceof StrutsContext sc) ? sc : null;
|
||||
if (strutsContext == null) {
|
||||
throw new IllegalArgumentException("Context must be a StrutsContext for OGNL 3.5.x+");
|
||||
}
|
||||
return typeConverter.convertValue(ognlContext, target, member, propertyName, value, toType);
|
||||
return typeConverter.convertValue(strutsContext, target, member, propertyName, value, toType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.apache.struts2.util.ValueStack;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
@@ -34,6 +33,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
@@ -64,7 +64,7 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceAccessor(OgnlContext context, Object target, Object index) {
|
||||
public String getSourceAccessor(StrutsContext context, Object target, Object index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceSetter(OgnlContext context, Object target, Object index) {
|
||||
public String getSourceSetter(StrutsContext context, Object target, Object index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
|
||||
for (Object o : root) {
|
||||
@@ -138,7 +138,7 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object name) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object name) throws OgnlException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
|
||||
if (name instanceof Integer index) {
|
||||
@@ -182,7 +182,7 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callMethod(OgnlContext context, Object target, String name, Object[] objects) throws MethodFailedException {
|
||||
public Object callMethod(StrutsContext context, Object target, String name, Object[] objects) throws MethodFailedException {
|
||||
CompoundRoot root = (CompoundRoot) target;
|
||||
|
||||
if ("describe".equals(name)) {
|
||||
@@ -270,12 +270,12 @@ public class CompoundRootAccessor implements RootAccessor, InternalDestroyable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callStaticMethod(OgnlContext transientVars, Class aClass, String s, Object[] objects) throws MethodFailedException {
|
||||
public Object callStaticMethod(StrutsContext transientVars, Class aClass, String s, Object[] objects) throws MethodFailedException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class classForName(String className, OgnlContext context) throws ClassNotFoundException {
|
||||
public Class classForName(String className, StrutsContext context) throws ClassNotFoundException {
|
||||
Object root = Ognl.getRoot(context);
|
||||
|
||||
if (disallowCustomOgnlMap) {
|
||||
|
||||
+4
-4
@@ -19,20 +19,20 @@
|
||||
package org.apache.struts2.ognl.accessor;
|
||||
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
public class HttpParametersPropertyAccessor extends ObjectPropertyAccessor {
|
||||
public class HttpParametersPropertyAccessor extends ObjectPropertyAccessor<StrutsContext> {
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object oname) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object oname) throws OgnlException {
|
||||
HttpParameters parameters = (HttpParameters) target;
|
||||
return parameters.get(String.valueOf(oname)).getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object oname, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object oname, Object value) throws OgnlException {
|
||||
throw new OgnlException("Access to " + target.getClass().getName() + " is read-only!");
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,12 @@ package org.apache.struts2.ognl.accessor;
|
||||
import org.apache.struts2.conversion.impl.XWorkConverter;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
public class ObjectAccessor extends ObjectPropertyAccessor {
|
||||
public class ObjectAccessor extends ObjectPropertyAccessor<StrutsContext> {
|
||||
@Override
|
||||
public Object getProperty(OgnlContext map, Object o, Object o1) throws OgnlException {
|
||||
public Object getProperty(StrutsContext map, Object o, Object o1) throws OgnlException {
|
||||
Object obj = super.getProperty(map, o, o1);
|
||||
|
||||
map.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, o.getClass());
|
||||
|
||||
+11
-9
@@ -20,10 +20,10 @@ package org.apache.struts2.ognl.accessor;
|
||||
|
||||
import org.apache.struts2.ognl.ObjectProxy;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
/**
|
||||
* Is able to access (set/get) properties on a given object.
|
||||
@@ -33,13 +33,13 @@ import ognl.PropertyAccessor;
|
||||
*
|
||||
* @author Gabe
|
||||
*/
|
||||
public class ObjectProxyPropertyAccessor implements PropertyAccessor {
|
||||
public class ObjectProxyPropertyAccessor implements PropertyAccessor<StrutsContext> {
|
||||
|
||||
/**
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceAccessor(OgnlContext context, Object target, Object index) {
|
||||
public String getSourceAccessor(StrutsContext context, Object target, Object index) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
@@ -47,25 +47,27 @@ public class ObjectProxyPropertyAccessor implements PropertyAccessor {
|
||||
* Used by OGNl to generate bytecode
|
||||
*/
|
||||
@Override
|
||||
public String getSourceSetter(OgnlContext context, Object target, Object index) {
|
||||
public String getSourceSetter(StrutsContext context, Object target, Object index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object name) throws OgnlException {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public Object getProperty(StrutsContext context, Object target, Object name) throws OgnlException {
|
||||
ObjectProxy proxy = (ObjectProxy) target;
|
||||
setupContext(context, proxy);
|
||||
|
||||
return OgnlRuntime.getPropertyAccessor(proxy.getValue().getClass()).getProperty(context, target, name);
|
||||
return ((PropertyAccessor) OgnlRuntime.getPropertyAccessor(proxy.getValue().getClass())).getProperty(context, target, name);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
ObjectProxy proxy = (ObjectProxy) target;
|
||||
setupContext(context, proxy);
|
||||
|
||||
OgnlRuntime.getPropertyAccessor(proxy.getValue().getClass()).setProperty(context, target, name, value);
|
||||
((PropertyAccessor) OgnlRuntime.getPropertyAccessor(proxy.getValue().getClass())).setProperty(context, target, name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +77,7 @@ public class ObjectProxyPropertyAccessor implements PropertyAccessor {
|
||||
* @param context
|
||||
* @param proxy
|
||||
*/
|
||||
private void setupContext(OgnlContext context, ObjectProxy proxy) {
|
||||
private void setupContext(StrutsContext context, ObjectProxy proxy) {
|
||||
ReflectionContextState.setLastBeanClassAccessed(context, proxy.getLastClassAccessed());
|
||||
ReflectionContextState.setLastBeanPropertyAccessed(context, proxy.getLastPropertyAccessed());
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
package org.apache.struts2.ognl.accessor;
|
||||
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
public class ParameterPropertyAccessor extends ObjectPropertyAccessor {
|
||||
public class ParameterPropertyAccessor extends ObjectPropertyAccessor<StrutsContext> {
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object oname) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object oname) throws OgnlException {
|
||||
if (target instanceof Parameter parameter) {
|
||||
if ("value".equalsIgnoreCase(String.valueOf(oname))) {
|
||||
throw new OgnlException("Access to " + oname + " is not allowed! Call parameter name directly!");
|
||||
@@ -37,7 +37,7 @@ public class ParameterPropertyAccessor extends ObjectPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object oname, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object oname, Object value) throws OgnlException {
|
||||
if (target instanceof Parameter) {
|
||||
throw new OgnlException("Access to " + target.getClass().getName() + " is read-only!");
|
||||
} else {
|
||||
|
||||
@@ -21,9 +21,10 @@ package org.apache.struts2.ognl.accessor;
|
||||
import ognl.ClassResolver;
|
||||
import ognl.MethodAccessor;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public interface RootAccessor extends PropertyAccessor, MethodAccessor, ClassResolver {
|
||||
public interface RootAccessor extends PropertyAccessor<StrutsContext>, MethodAccessor<StrutsContext>, ClassResolver<StrutsContext> {
|
||||
}
|
||||
|
||||
+7
-7
@@ -25,10 +25,10 @@ import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.ognl.OgnlUtil;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.SetPropertyAccessor;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -40,7 +40,7 @@ import java.util.Map;
|
||||
/**
|
||||
* @author Gabe
|
||||
*/
|
||||
public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor<StrutsContext> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(XWorkCollectionPropertyAccessor.class);
|
||||
|
||||
@@ -87,7 +87,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
* @see ognl.PropertyAccessor#getProperty(java.util.Map, Object, Object)
|
||||
*/
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object key) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object key) throws OgnlException {
|
||||
LOG.trace("Entering getProperty()");
|
||||
|
||||
//check if it is a generic type property.
|
||||
@@ -186,7 +186,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
* Gets an indexed Map by a given key property with the key being
|
||||
* the value of the property and the value being the
|
||||
*/
|
||||
private Map getSetMap(OgnlContext context, Collection collection, String property) throws OgnlException {
|
||||
private Map getSetMap(StrutsContext context, Collection collection, String property) throws OgnlException {
|
||||
LOG.trace("getting set Map");
|
||||
|
||||
String path = ReflectionContextState.getCurrentPropertyPath(context);
|
||||
@@ -211,7 +211,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
/*
|
||||
* gets a bean with the given
|
||||
*/
|
||||
public Object getPropertyThroughIteration(OgnlContext context, Collection collection, String property, Object key)
|
||||
public Object getPropertyThroughIteration(StrutsContext context, Collection collection, String property, Object key)
|
||||
throws OgnlException {
|
||||
//TODO
|
||||
for (Object currTest : collection) {
|
||||
@@ -224,7 +224,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
|
||||
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
|
||||
Class convertToClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
|
||||
@@ -256,7 +256,7 @@ public class XWorkCollectionPropertyAccessor extends SetPropertyAccessor {
|
||||
super.setProperty(context, target, name, realValue);
|
||||
}
|
||||
|
||||
private Object getRealValue(OgnlContext context, Object value, Class convertToClass) {
|
||||
private Object getRealValue(StrutsContext context, Object value, Class convertToClass) {
|
||||
if (value == null || convertToClass == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ package org.apache.struts2.ognl.accessor;
|
||||
|
||||
import ognl.EnumerationPropertyAccessor;
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
public class XWorkEnumerationAccessor extends EnumerationPropertyAccessor {
|
||||
public class XWorkEnumerationAccessor extends EnumerationPropertyAccessor<StrutsContext> {
|
||||
|
||||
private final ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
private final ObjectPropertyAccessor<StrutsContext> opa = new ObjectPropertyAccessor<>();
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
opa.setProperty(context, target, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -20,15 +20,15 @@ package org.apache.struts2.ognl.accessor;
|
||||
|
||||
import ognl.IteratorPropertyAccessor;
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
public class XWorkIteratorPropertyAccessor extends IteratorPropertyAccessor {
|
||||
public class XWorkIteratorPropertyAccessor extends IteratorPropertyAccessor<StrutsContext> {
|
||||
|
||||
private final ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
|
||||
private final ObjectPropertyAccessor<StrutsContext> opa = new ObjectPropertyAccessor<>();
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
opa.setProperty(context, target, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.ognl.OgnlUtil;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.ListPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsException;
|
||||
|
||||
@@ -41,7 +41,7 @@ import java.util.List;
|
||||
*
|
||||
* @author Gabriel Zimmerman
|
||||
*/
|
||||
public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
public class XWorkListPropertyAccessor extends ListPropertyAccessor<StrutsContext> {
|
||||
|
||||
private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
|
||||
|
||||
@@ -57,7 +57,7 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
}
|
||||
|
||||
@Inject("java.util.Collection")
|
||||
public void setXWorkCollectionPropertyAccessor(PropertyAccessor acc) {
|
||||
public void setXWorkCollectionPropertyAccessor(PropertyAccessor<StrutsContext> acc) {
|
||||
this._sAcc = (XWorkCollectionPropertyAccessor) acc;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object name) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object name) throws OgnlException {
|
||||
|
||||
if (ReflectionContextState.isGettingByKeyProperty(context)
|
||||
|| name.equals(XWorkCollectionPropertyAccessor.KEY_PROPERTY_FOR_CREATION)) {
|
||||
@@ -137,7 +137,7 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value)
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value)
|
||||
throws OgnlException {
|
||||
|
||||
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
|
||||
@@ -185,7 +185,7 @@ public class XWorkListPropertyAccessor extends ListPropertyAccessor {
|
||||
super.setProperty(context, target, name, realValue);
|
||||
}
|
||||
|
||||
private Object getRealValue(OgnlContext context, Object value, Class convertToClass) {
|
||||
private Object getRealValue(StrutsContext context, Object value, Class convertToClass) {
|
||||
if (value == null || convertToClass == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import org.apache.struts2.conversion.impl.XWorkConverter;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.MapPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -37,7 +37,7 @@ import java.util.Map;
|
||||
*
|
||||
* @author Gabriel Zimmerman
|
||||
*/
|
||||
public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
public class XWorkMapPropertyAccessor extends MapPropertyAccessor<StrutsContext> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(XWorkMapPropertyAccessor.class);
|
||||
|
||||
@@ -63,7 +63,7 @@ public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object name) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object name) throws OgnlException {
|
||||
LOG.trace("Entering getProperty ({},{},{})", context, target, name);
|
||||
|
||||
ReflectionContextState.updateCurrentPropertyPath(context, name);
|
||||
@@ -123,7 +123,7 @@ public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperty(OgnlContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
public void setProperty(StrutsContext context, Object target, Object name, Object value) throws OgnlException {
|
||||
LOG.trace("Entering setProperty({},{},{},{})", context, target, name, value);
|
||||
|
||||
Object key = getKey(context, name);
|
||||
@@ -131,7 +131,7 @@ public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
map.put(key, getValue(context, value));
|
||||
}
|
||||
|
||||
private Object getValue(OgnlContext context, Object value) {
|
||||
private Object getValue(StrutsContext context, Object value) {
|
||||
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
|
||||
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
|
||||
if (lastClass == null || lastProperty == null) {
|
||||
@@ -144,7 +144,7 @@ public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
|
||||
return xworkConverter.convertValue(context, value, elementClass);
|
||||
}
|
||||
|
||||
private Object getKey(OgnlContext context, Object name) {
|
||||
private Object getKey(StrutsContext context, Object name) {
|
||||
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
|
||||
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
|
||||
if (lastClass == null || lastProperty == null) {
|
||||
|
||||
@@ -21,9 +21,9 @@ package org.apache.struts2.ognl.accessor;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.MethodFailedException;
|
||||
import ognl.ObjectMethodAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.PropertyAccessor;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -38,12 +38,13 @@ import java.util.Collection;
|
||||
* @author Patrick Lightbody
|
||||
* @author tmjee
|
||||
*/
|
||||
public class XWorkMethodAccessor extends ObjectMethodAccessor {
|
||||
public class XWorkMethodAccessor extends ObjectMethodAccessor<StrutsContext> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(XWorkMethodAccessor.class);
|
||||
|
||||
@Override
|
||||
public Object callMethod(OgnlContext context, Object object, String string, Object[] objects) throws MethodFailedException {
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object callMethod(StrutsContext context, Object object, String string, Object[] objects) throws MethodFailedException {
|
||||
|
||||
//Collection property accessing
|
||||
//this if statement ensures that ognl
|
||||
@@ -94,7 +95,7 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
private Object callMethodWithDebugInfo(OgnlContext context, Object object, String methodName, Object[] objects) throws MethodFailedException {
|
||||
private Object callMethodWithDebugInfo(StrutsContext context, Object object, String methodName, Object[] objects) throws MethodFailedException {
|
||||
try {
|
||||
return super.callMethod(context, object, methodName, objects);
|
||||
} catch (MethodFailedException e) {
|
||||
@@ -109,7 +110,7 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callStaticMethod(OgnlContext context, Class aClass, String string, Object[] objects) throws MethodFailedException {
|
||||
public Object callStaticMethod(StrutsContext context, Class aClass, String string, Object[] objects) throws MethodFailedException {
|
||||
boolean e = ReflectionContextState.isDenyMethodExecution(context);
|
||||
|
||||
if (!e) {
|
||||
@@ -119,7 +120,7 @@ public class XWorkMethodAccessor extends ObjectMethodAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
private Object callStaticMethodWithDebugInfo(OgnlContext context, Class aClass, String methodName,
|
||||
private Object callStaticMethodWithDebugInfo(StrutsContext context, Class aClass, String methodName,
|
||||
Object[] objects) throws MethodFailedException {
|
||||
try {
|
||||
return super.callStaticMethod(context, aClass, methodName, objects);
|
||||
|
||||
+3
-3
@@ -21,15 +21,15 @@ package org.apache.struts2.ognl.accessor;
|
||||
import org.apache.struts2.conversion.impl.XWorkConverter;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.ObjectPropertyAccessor;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
/**
|
||||
* @author Gabe
|
||||
*/
|
||||
public class XWorkObjectPropertyAccessor extends ObjectPropertyAccessor {
|
||||
public class XWorkObjectPropertyAccessor extends ObjectPropertyAccessor<StrutsContext> {
|
||||
@Override
|
||||
public Object getProperty(OgnlContext context, Object target, Object oname) throws OgnlException {
|
||||
public Object getProperty(StrutsContext context, Object target, Object oname) throws OgnlException {
|
||||
//set the last set objects in the context
|
||||
//so if the next objects accessed are
|
||||
//Maps or Collections they can use the information
|
||||
|
||||
+2
-2
@@ -42,7 +42,7 @@ import org.apache.struts2.ognl.accessor.RootAccessor;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.ValueStackFactory;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import org.apache.struts2.action.NoParameters;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import org.apache.struts2.action.ParameterValueAware;
|
||||
@@ -353,7 +353,7 @@ public class ParametersInterceptorTest extends XWorkTestCase {
|
||||
//then
|
||||
assertEquals("This is blah", ((SimpleAction) proxy.getAction()).getBlah());
|
||||
Field field = ReflectionContextState.class.getField("DENY_METHOD_EXECUTION");
|
||||
boolean allowStaticFieldAccess = ((OgnlContext) stack.getContext()).getMemberAccess().isAccessible((OgnlContext) stack.getContext(), ReflectionContextState.class, field, "");
|
||||
boolean allowStaticFieldAccess = ((StrutsContext) stack.getContext()).getMemberAccess().isAccessible((StrutsContext) stack.getContext(), ReflectionContextState.class, field, "");
|
||||
assertFalse(allowStaticFieldAccess);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import ognl.MethodFailedException;
|
||||
import ognl.NoSuchPropertyException;
|
||||
import ognl.NullHandler;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
import ognl.OgnlException;
|
||||
import ognl.OgnlRuntime;
|
||||
import ognl.SimpleNode;
|
||||
@@ -90,12 +89,12 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
public void testCanSetADependentObject() {
|
||||
String dogName = "fido";
|
||||
|
||||
OgnlRuntime.setNullHandler(Owner.class, new NullHandler() {
|
||||
public Object nullMethodResult(OgnlContext context, Object o, String s, Object[] objects) {
|
||||
OgnlRuntime.setNullHandler(Owner.class, new NullHandler<StrutsContext>() {
|
||||
public Object nullMethodResult(StrutsContext context, Object o, String s, Object[] objects) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object nullPropertyValue(OgnlContext context, Object o, Object o1) {
|
||||
public Object nullPropertyValue(StrutsContext context, Object o, Object o1) {
|
||||
String methodName = o1.toString();
|
||||
String getter = "set" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
|
||||
Method[] methods = o.getClass().getDeclaredMethods();
|
||||
@@ -199,7 +198,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
|
||||
public void testExpressionIsCachedIrrespectiveOfItsExecutionStatus() {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = ognlUtil.createDefaultContext(foo);
|
||||
StrutsContext context = ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Expression which executes with success
|
||||
try {
|
||||
@@ -223,7 +222,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
ognlUtil.setContainer(container); // Must be explicitly set as the generated OgnlUtil instance has no container
|
||||
ognlUtil.setEnableExpressionCache("true");
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = ognlUtil.createDefaultContext(foo);
|
||||
StrutsContext context = ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Expression which executes with success
|
||||
try {
|
||||
@@ -243,7 +242,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
|
||||
public void testMethodExpressionIsCachedIrrespectiveOfItsExecutionStatus() {
|
||||
Foo foo = new Foo();
|
||||
OgnlContext context = ognlUtil.createDefaultContext(foo);
|
||||
StrutsContext context = ognlUtil.createDefaultContext(foo);
|
||||
|
||||
// Method expression which executes with success
|
||||
try {
|
||||
@@ -846,7 +845,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
ChainingInterceptor foo = new ChainingInterceptor();
|
||||
ChainingInterceptor foo2 = new ChainingInterceptor();
|
||||
|
||||
OgnlContext context = ognlUtil.createDefaultContext(null);
|
||||
StrutsContext context = ognlUtil.createDefaultContext(null);
|
||||
SimpleNode expression = (SimpleNode) Ognl.parseExpression("{'a','ruby','b','tom'}");
|
||||
|
||||
Ognl.getValue(expression, context, "aksdj");
|
||||
@@ -903,7 +902,7 @@ public class OgnlUtilTest extends XWorkTestCase {
|
||||
public void testBeanMapExpressions() throws OgnlException, NoSuchMethodException {
|
||||
Foo foo = new Foo();
|
||||
|
||||
OgnlContext context = ognlUtil.createDefaultContext(foo);
|
||||
StrutsContext context = ognlUtil.createDefaultContext(foo);
|
||||
SecurityMemberAccess sma = (SecurityMemberAccess) context.getMemberAccess();
|
||||
|
||||
sma.useExcludedPackageNames("org.apache.struts2.ognl");
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.MemberAccess;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.apache.struts2.TestBean;
|
||||
import org.apache.struts2.config.ConfigurationException;
|
||||
@@ -55,7 +54,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
public class SecurityMemberAccessTest {
|
||||
|
||||
private OgnlContext context;
|
||||
private StrutsContext context;
|
||||
private FooBar target;
|
||||
protected SecurityMemberAccess sma;
|
||||
protected ProviderAllowlist mockedProviderAllowlist;
|
||||
@@ -64,12 +63,12 @@ public class SecurityMemberAccessTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context = ognl.Ognl.createDefaultContext(null);
|
||||
target = new FooBar();
|
||||
mockedProviderAllowlist = mock(ProviderAllowlist.class);
|
||||
mockedThreadAllowlist = mock(ThreadAllowlist.class);
|
||||
proxyService = new StrutsProxyService(new StrutsProxyCacheFactory<>("1000", "basic"));
|
||||
assignNewSma(true);
|
||||
context = new StrutsContext(sma);
|
||||
}
|
||||
|
||||
protected void assignNewSma(boolean allowStaticFieldAccess) {
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.location.LocatableProperties;
|
||||
import org.apache.struts2.util.reflection.ReflectionContextState;
|
||||
import ognl.Ognl;
|
||||
import ognl.OgnlContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -57,7 +56,8 @@ public class SetPropertiesTest extends XWorkTestCase {
|
||||
|
||||
public void testOgnlUtilEmptyStringAsLong() {
|
||||
Bar bar = new Bar();
|
||||
OgnlContext context = Ognl.createDefaultContext(bar, new SecurityMemberAccess(null, null));
|
||||
StrutsContext context = new StrutsContext(new SecurityMemberAccess(null, null));
|
||||
context.withRoot(bar);
|
||||
context.put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
|
||||
bar.setId(null);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class SetPropertiesTest extends XWorkTestCase {
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.getContext().put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
|
||||
|
||||
XWorkConverter c = (XWorkConverter) ((OgnlTypeConverterWrapper) Ognl.getTypeConverter((OgnlContext) vs.getContext())).getTarget();
|
||||
XWorkConverter c = (XWorkConverter) ((OgnlTypeConverterWrapper) ((StrutsContext) vs.getContext()).getTypeConverter()).getTarget();
|
||||
c.registerConverter(Cat.class.getName(), new FooBarConverter());
|
||||
vs.push(foo);
|
||||
|
||||
@@ -97,7 +97,7 @@ public class SetPropertiesTest extends XWorkTestCase {
|
||||
ValueStack vs = ActionContext.getContext().getValueStack();
|
||||
vs.getContext().put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);
|
||||
|
||||
XWorkConverter c = (XWorkConverter) ((OgnlTypeConverterWrapper) Ognl.getTypeConverter((OgnlContext) vs.getContext())).getTarget();
|
||||
XWorkConverter c = (XWorkConverter) ((OgnlTypeConverterWrapper) ((StrutsContext) vs.getContext()).getTypeConverter()).getTarget();
|
||||
c.registerConverter(Cat.class.getName(), new FooBarConverter());
|
||||
vs.push(foo);
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
import ognl.ClassResolver;
|
||||
import ognl.MemberAccess;
|
||||
import ognl.TypeConverter;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class StrutsContextTest {
|
||||
|
||||
@Test
|
||||
public void shouldCreateContextWithRequiredMemberAccess() {
|
||||
MemberAccess<StrutsContext> memberAccess = mock(MemberAccess.class);
|
||||
var context = new StrutsContext(memberAccess);
|
||||
|
||||
assertThat(context).isNotNull();
|
||||
assertThat(context.getMemberAccess()).isSameAs(memberAccess);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateContextWithAllComponents() {
|
||||
MemberAccess<StrutsContext> memberAccess = mock(MemberAccess.class);
|
||||
ClassResolver<StrutsContext> classResolver = mock(ClassResolver.class);
|
||||
TypeConverter<StrutsContext> typeConverter = mock(TypeConverter.class);
|
||||
|
||||
var context = new StrutsContext(memberAccess, classResolver, typeConverter);
|
||||
|
||||
assertThat(context.getMemberAccess()).isSameAs(memberAccess);
|
||||
assertThat(context.getClassResolver()).isSameAs(classResolver);
|
||||
assertThat(context.getTypeConverter()).isSameAs(typeConverter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportRootObject() {
|
||||
MemberAccess<StrutsContext> memberAccess = mock(MemberAccess.class);
|
||||
var root = new Object();
|
||||
var context = new StrutsContext(memberAccess);
|
||||
context.withRoot(root);
|
||||
|
||||
assertThat(context.getRoot()).isSameAs(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldImplementMapInterface() {
|
||||
MemberAccess<StrutsContext> memberAccess = mock(MemberAccess.class);
|
||||
var context = new StrutsContext(memberAccess);
|
||||
|
||||
context.put("testKey", "testValue");
|
||||
assertThat(context.get("testKey")).isEqualTo("testValue");
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@
|
||||
package org.apache.struts2.util;
|
||||
|
||||
import org.apache.struts2.ognl.SecurityMemberAccess;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
import jakarta.servlet.jsp.tagext.TagSupport;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.StrutsInternalTestCase;
|
||||
import org.apache.struts2.views.jsp.ActionTag;
|
||||
|
||||
@@ -28,12 +28,12 @@ import java.lang.reflect.Member;
|
||||
|
||||
public class SecurityMemberAccessInServletsTest extends StrutsInternalTestCase {
|
||||
|
||||
private OgnlContext context;
|
||||
private StrutsContext context;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context = ognl.Ognl.createDefaultContext(null);
|
||||
context = new StrutsContext(new SecurityMemberAccess(null, null));
|
||||
}
|
||||
|
||||
public void testJavaxServletPackageAccess() throws Exception {
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
# Hibernate Proxy Detection Optimization
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Eliminate `LinkageError` exceptions thrown when Hibernate is not on the classpath by detecting availability once at class-load time.
|
||||
|
||||
**Architecture:** Add a static availability check in `StrutsProxyService` that probes for `org.hibernate.proxy.HibernateProxy` once during class initialization. All Hibernate-related methods short-circuit immediately when Hibernate is absent. Same pattern applied to deprecated `ProxyUtil`.
|
||||
|
||||
**Tech Stack:** Java 17, JUnit 5, AssertJ, Mockito
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add Hibernate Availability Check to StrutsProxyService
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/src/main/java/org/apache/struts2/util/StrutsProxyService.java`
|
||||
- Test: `core/src/test/java/org/apache/struts2/util/StrutsProxyServiceTest.java`
|
||||
|
||||
- [ ] **Step 1: Write the failing test — verify no LinkageError is thrown when Hibernate classes are used**
|
||||
|
||||
The existing tests already call `isHibernateProxy()` and `isHibernateProxyMember()` with non-Hibernate objects. We need a test that verifies the short-circuit behavior works correctly. Add this test to `StrutsProxyServiceTest.java`:
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void isHibernateProxyDoesNotThrowWhenCalledRepeatedly() {
|
||||
// Verify that calling isHibernateProxy many times for different objects
|
||||
// does not cause performance issues (no exceptions thrown internally)
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
assertThat(proxyService.isHibernateProxy(new Object())).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isHibernateProxyMemberDoesNotThrowWhenCalledRepeatedly() throws NoSuchMethodException {
|
||||
Method method = Object.class.getMethod("toString");
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
assertThat(proxyService.isHibernateProxyMember(method)).isFalse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they pass (baseline — these pass even without the fix because Hibernate IS on the test classpath)**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core -Dtest=StrutsProxyServiceTest#isHibernateProxyDoesNotThrowWhenCalledRepeatedly+isHibernateProxyMemberDoesNotThrowWhenCalledRepeatedly`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 3: Add static Hibernate availability flag to StrutsProxyService**
|
||||
|
||||
In `core/src/main/java/org/apache/struts2/util/StrutsProxyService.java`, add a static availability check at the top of the class and modify the three Hibernate methods to short-circuit:
|
||||
|
||||
```java
|
||||
// Add this field near the top of the class, after the class declaration:
|
||||
private static final boolean HIBERNATE_AVAILABLE = isHibernateAvailable();
|
||||
|
||||
private static boolean isHibernateAvailable() {
|
||||
try {
|
||||
Class.forName("org.hibernate.proxy.HibernateProxy");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then modify the three Hibernate methods to short-circuit:
|
||||
|
||||
**`isHibernateProxy`** — change from:
|
||||
```java
|
||||
@Override
|
||||
public boolean isHibernateProxy(Object object) {
|
||||
try {
|
||||
return object != null && HibernateProxy.class.isAssignableFrom(object.getClass());
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
to:
|
||||
```java
|
||||
@Override
|
||||
public boolean isHibernateProxy(Object object) {
|
||||
if (!HIBERNATE_AVAILABLE || object == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return HibernateProxy.class.isAssignableFrom(object.getClass());
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`isHibernateProxyMember`** — change from:
|
||||
```java
|
||||
@Override
|
||||
public boolean isHibernateProxyMember(Member member) {
|
||||
try {
|
||||
return hasMember(HibernateProxy.class, member);
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
to:
|
||||
```java
|
||||
@Override
|
||||
public boolean isHibernateProxyMember(Member member) {
|
||||
if (!HIBERNATE_AVAILABLE) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return hasMember(HibernateProxy.class, member);
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`getHibernateProxyTarget`** — change from:
|
||||
```java
|
||||
@Override
|
||||
public Object getHibernateProxyTarget(Object object) {
|
||||
try {
|
||||
return Hibernate.unproxy(object);
|
||||
} catch (LinkageError ignored) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
```
|
||||
to:
|
||||
```java
|
||||
@Override
|
||||
public Object getHibernateProxyTarget(Object object) {
|
||||
if (!HIBERNATE_AVAILABLE) {
|
||||
return object;
|
||||
}
|
||||
try {
|
||||
return Hibernate.unproxy(object);
|
||||
} catch (LinkageError ignored) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the full StrutsProxyService test suite**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core -Dtest=StrutsProxyServiceTest`
|
||||
Expected: All tests PASS
|
||||
|
||||
- [ ] **Step 5: Run the Spring integration test suite**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core -Dtest=StrutsProxyServiceSpringIntegrationTest`
|
||||
Expected: All tests PASS
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add core/src/main/java/org/apache/struts2/util/StrutsProxyService.java core/src/test/java/org/apache/struts2/util/StrutsProxyServiceTest.java
|
||||
git commit -m "WW-5622 Optimize Hibernate proxy detection to avoid LinkageError exceptions
|
||||
|
||||
Add static availability check for Hibernate classes in StrutsProxyService.
|
||||
When Hibernate is not on the classpath, all Hibernate-related methods
|
||||
short-circuit immediately without throwing/catching LinkageError.
|
||||
This eliminates a significant performance penalty for applications
|
||||
that don't use Hibernate."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Apply Same Fix to Deprecated ProxyUtil
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/src/main/java/org/apache/struts2/util/ProxyUtil.java`
|
||||
|
||||
- [ ] **Step 1: Add the same static availability check to ProxyUtil**
|
||||
|
||||
In `core/src/main/java/org/apache/struts2/util/ProxyUtil.java`, add the same pattern:
|
||||
|
||||
```java
|
||||
// Add after the isProxyMemberCache field:
|
||||
private static final boolean HIBERNATE_AVAILABLE = isHibernateAvailable();
|
||||
|
||||
private static boolean isHibernateAvailable() {
|
||||
try {
|
||||
Class.forName("org.hibernate.proxy.HibernateProxy");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then modify the three Hibernate methods in ProxyUtil identically to Task 1:
|
||||
|
||||
**`isHibernateProxy`**:
|
||||
```java
|
||||
@Deprecated(since = "7.2")
|
||||
public static boolean isHibernateProxy(Object object) {
|
||||
if (!HIBERNATE_AVAILABLE || object == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return HibernateProxy.class.isAssignableFrom(object.getClass());
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`isHibernateProxyMember`**:
|
||||
```java
|
||||
@Deprecated(since = "7.2")
|
||||
public static boolean isHibernateProxyMember(Member member) {
|
||||
if (!HIBERNATE_AVAILABLE) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return hasMember(HibernateProxy.class, member);
|
||||
} catch (LinkageError ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`getHibernateProxyTarget`**:
|
||||
```java
|
||||
@Deprecated(since = "7.2")
|
||||
public static Object getHibernateProxyTarget(Object object) {
|
||||
if (!HIBERNATE_AVAILABLE) {
|
||||
return object;
|
||||
}
|
||||
try {
|
||||
return Hibernate.unproxy(object);
|
||||
} catch (LinkageError ignored) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run existing ProxyUtil tests**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core -Dtest=ProxyUtilTest`
|
||||
Expected: PASS (or if no dedicated test exists, run the SecurityMemberAccess tests which exercise ProxyUtil indirectly)
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core -Dtest=SecurityMemberAccessTest`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add core/src/main/java/org/apache/struts2/util/ProxyUtil.java
|
||||
git commit -m "WW-5622 Apply same Hibernate availability optimization to deprecated ProxyUtil"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Run Full Test Suite
|
||||
|
||||
- [ ] **Step 1: Run all core tests**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl core`
|
||||
Expected: All tests PASS
|
||||
|
||||
- [ ] **Step 2: Run spring plugin tests (exercises proxy detection heavily)**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl plugins/spring`
|
||||
Expected: All tests PASS
|
||||
|
||||
- [ ] **Step 3: Run json plugin tests (StrutsJSONWriter has Hibernate-related class name checks)**
|
||||
|
||||
Run: `mvn test -DskipAssembly -pl plugins/json`
|
||||
Expected: All tests PASS
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
# OGNL 3.5.x Upgrade — Design Spec
|
||||
|
||||
> **Jira:** [WW-5326](https://issues.apache.org/jira/browse/WW-5326)
|
||||
|
||||
## Goal
|
||||
|
||||
Upgrade Apache Struts from OGNL 3.4.10 to OGNL 3.5.0-BETA4+ and introduce `StrutsContext extends OgnlContext<StrutsContext>` as the framework's own OGNL evaluation context. This lays the foundation for treating OGNL as an execution sandbox with typed, Struts-specific context state.
|
||||
|
||||
## Motivation
|
||||
|
||||
- **Forward-looking maintenance**: stay current with OGNL development, avoid a larger migration later
|
||||
- **Real-world validation**: Struts is the primary consumer of OGNL — upgrading validates the 3.5.x generic API
|
||||
- **Java 17 baseline**: OGNL 3.5.x requires Java 17, aligning with Struts 7.x
|
||||
- **Type safety**: self-bounded generics (`OgnlContext<C>`) enable Struts to have a properly typed context instead of stringly-typed map entries
|
||||
- **Sandbox foundation**: `StrutsContext` is the first step toward isolated OGNL evaluation contexts (`OgnlRuntime` instance-based isolation is future work)
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Instance-based `OgnlRuntime` / true sandbox isolation (future OGNL work)
|
||||
- Consuming new OGNL features (null-safe operator `?.`, dual-mode evaluation) — those come as separate follow-ups
|
||||
- Behavioral changes to security model, accessor logic, or expression evaluation
|
||||
|
||||
## Current State
|
||||
|
||||
### OGNL Usage in Struts
|
||||
|
||||
- **Version**: 3.4.10 (defined in root `pom.xml` as `ognl.version`)
|
||||
- **Core dependency**: `core/pom.xml` depends on `ognl:ognl`
|
||||
- **Context creation**: 3 call sites use `Ognl.createDefaultContext()`:
|
||||
- `OgnlUtil.createDefaultContext()` (line 738)
|
||||
- `OgnlValueStack.setRoot()` (line 124)
|
||||
- `OgnlReflectionContextFactory.createDefaultContext()` (line 33)
|
||||
- **No custom OgnlContext subclass**: Struts uses `OgnlContext` directly
|
||||
- **Context state via map entries**: flags like `DENY_METHOD_EXECUTION`, `CREATE_NULL_OBJECTS`, `VALUE_STACK`, conversion state — all stored as stringly-typed map entries in `OgnlContext` and accessed via `ReflectionContextState` static methods
|
||||
|
||||
### OGNL Interface Implementations in Struts
|
||||
|
||||
| Interface | Struts Implementation |
|
||||
|---|---|
|
||||
| `MemberAccess` | `SecurityMemberAccess` |
|
||||
| `TypeConverter` | `OgnlTypeConverterWrapper` |
|
||||
| `ClassResolver` | `RootAccessor` (interface), `CompoundRootAccessor` (impl) |
|
||||
| `PropertyAccessor` | `RootAccessor`, `CompoundRootAccessor`, `ObjectProxyPropertyAccessor`, + 8 classes extending `ObjectPropertyAccessor`/`ListPropertyAccessor`/`MapPropertyAccessor`/etc. |
|
||||
| `MethodAccessor` | `RootAccessor`, `CompoundRootAccessor`, `XWorkMethodAccessor` |
|
||||
| `NullHandler` | `OgnlNullHandlerWrapper` |
|
||||
|
||||
### Tiles Plugin OGNL Usage
|
||||
|
||||
6-8 files in `plugins/tiles` use OGNL directly:
|
||||
- `ScopePropertyAccessor`, `AnyScopePropertyAccessor`, `NestedObjectDelegatePropertyAccessor`, `DelegatePropertyAccessor`
|
||||
- `OGNLAttributeEvaluator`, `PropertyAccessorDelegateFactory`, `TilesContextPropertyAccessorDelegateFactory`
|
||||
- Associated test files
|
||||
|
||||
## OGNL 3.5.x Key API Changes
|
||||
|
||||
### Self-Bounded Generics
|
||||
|
||||
All core interfaces and classes are now generic with `<C extends OgnlContext<C>>`:
|
||||
|
||||
```java
|
||||
public class OgnlContext<C extends OgnlContext<C>> implements Map<String, Object>
|
||||
public interface MemberAccess<C extends OgnlContext<C>>
|
||||
public interface ClassResolver<C extends OgnlContext<C>>
|
||||
public interface TypeConverter<C extends OgnlContext<C>>
|
||||
public interface PropertyAccessor<C extends OgnlContext<C>>
|
||||
public interface MethodAccessor<C extends OgnlContext<C>>
|
||||
public interface NullHandler<C extends OgnlContext<C>>
|
||||
public class ObjectPropertyAccessor<C extends OgnlContext<C>> implements PropertyAccessor<C>
|
||||
// ... all base accessor classes similarly parameterized
|
||||
```
|
||||
|
||||
### OgnlContext Constructor Changes
|
||||
|
||||
```java
|
||||
// New (memberAccess first, required non-null)
|
||||
public OgnlContext(MemberAccess<C> memberAccess, ClassResolver<C> classResolver, TypeConverter<C> typeConverter)
|
||||
|
||||
// Deprecated (old parameter order)
|
||||
@Deprecated(forRemoval = true)
|
||||
OgnlContext(ClassResolver<C> classResolver, TypeConverter<C> typeConverter, MemberAccess<C> memberAccess)
|
||||
```
|
||||
|
||||
### OgnlContext.Builder
|
||||
|
||||
```java
|
||||
public static class Builder<C extends OgnlContext<C>> {
|
||||
public Builder(Function<Builder<C>, C> provider)
|
||||
public Builder<C> withMemberAccess(MemberAccess<C> memberAccess)
|
||||
public Builder<C> withClassResolver(ClassResolver<C> classResolver)
|
||||
public Builder<C> withTypeConverter(TypeConverter<C> converter)
|
||||
public Builder<C> withRoot(Object value)
|
||||
public C build()
|
||||
}
|
||||
```
|
||||
|
||||
### Other Changes
|
||||
|
||||
- `SecurityManager` support removed
|
||||
- Null-safe navigation operator (`?.`) added
|
||||
- `setRoot()` deprecated in favor of `withRoot()` (fluent)
|
||||
- Java 17 baseline
|
||||
|
||||
### Unchanged
|
||||
|
||||
- `Ognl` class remains abstract with only static methods (no instance-based evaluation)
|
||||
- `OgnlRuntime` remains a static utility (global accessor/cache registration)
|
||||
|
||||
## Design
|
||||
|
||||
### Approach: Direct StrutsContext Construction
|
||||
|
||||
Struts creates `StrutsContext` directly, bypassing `Ognl.createDefaultContext()`. This gives Struts full ownership of context lifecycle and avoids the global-state issues of `Ognl.withBuilderProvider()`.
|
||||
|
||||
### StrutsContext
|
||||
|
||||
```java
|
||||
package org.apache.struts2.ognl;
|
||||
|
||||
public class StrutsContext extends OgnlContext<StrutsContext> {
|
||||
|
||||
// Phase 1: just the constructor, delegate to super
|
||||
public StrutsContext(SecurityMemberAccess memberAccess,
|
||||
RootAccessor resolver,
|
||||
OgnlTypeConverterWrapper converter) {
|
||||
super(memberAccess, resolver, converter);
|
||||
}
|
||||
|
||||
// Phase 2 (incremental): promote map entries to typed fields
|
||||
// private ValueStack valueStack;
|
||||
// private boolean reportErrorsOnNoProperty;
|
||||
// private boolean throwExceptionOnFailure;
|
||||
// private boolean createNullObjects;
|
||||
// private boolean denyMethodExecution;
|
||||
// private boolean denyIndexedAccessExecution;
|
||||
// private String conversionPropertyFullName;
|
||||
// private String currentPropertyPath;
|
||||
// private Class<?> lastBeanClassAccessed;
|
||||
// private String lastBeanPropertyAccessed;
|
||||
}
|
||||
```
|
||||
|
||||
Phase 1 introduces the class with zero behavioral change — it's just an `OgnlContext` subclass. The typed fields are a follow-up.
|
||||
|
||||
### Generic Type Ripple
|
||||
|
||||
All OGNL interface implementations parameterize with `<StrutsContext>`:
|
||||
|
||||
```java
|
||||
// Core interfaces
|
||||
public class SecurityMemberAccess implements MemberAccess<StrutsContext>
|
||||
public class OgnlTypeConverterWrapper implements ognl.TypeConverter<StrutsContext>
|
||||
public interface RootAccessor extends PropertyAccessor<StrutsContext>, MethodAccessor<StrutsContext>, ClassResolver<StrutsContext>
|
||||
public class OgnlNullHandlerWrapper implements ognl.NullHandler<StrutsContext>
|
||||
|
||||
// Accessors (extend generic base classes)
|
||||
public class CompoundRootAccessor implements RootAccessor
|
||||
public class ObjectProxyPropertyAccessor implements PropertyAccessor<StrutsContext>
|
||||
public class ObjectAccessor extends ObjectPropertyAccessor<StrutsContext>
|
||||
public class ParameterPropertyAccessor extends ObjectPropertyAccessor<StrutsContext>
|
||||
public class HttpParametersPropertyAccessor extends ObjectPropertyAccessor<StrutsContext>
|
||||
public class XWorkObjectPropertyAccessor extends ObjectPropertyAccessor<StrutsContext>
|
||||
public class XWorkEnumerationAccessor extends EnumerationPropertyAccessor<StrutsContext> // verify base class
|
||||
public class XWorkIteratorPropertyAccessor extends IteratorPropertyAccessor<StrutsContext> // verify base class
|
||||
public class XWorkCollectionPropertyAccessor extends ObjectPropertyAccessor<StrutsContext>
|
||||
public class XWorkListPropertyAccessor extends ListPropertyAccessor<StrutsContext>
|
||||
public class XWorkMapPropertyAccessor extends MapPropertyAccessor<StrutsContext>
|
||||
public class XWorkMethodAccessor extends ObjectMethodAccessor<StrutsContext>
|
||||
```
|
||||
|
||||
Method signatures change `OgnlContext` parameters to `StrutsContext` throughout.
|
||||
|
||||
### Context Creation
|
||||
|
||||
Replace `Ognl.createDefaultContext()` with direct construction:
|
||||
|
||||
```java
|
||||
// OgnlUtil.createDefaultContext()
|
||||
protected StrutsContext createDefaultContext(Object root, ClassResolver<StrutsContext> resolver) {
|
||||
if (resolver == null) {
|
||||
resolver = container.getInstance(RootAccessor.class);
|
||||
}
|
||||
StrutsContext ctx = new StrutsContext(
|
||||
container.getInstance(SecurityMemberAccess.class), resolver, defaultConverter);
|
||||
ctx.withRoot(root);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// OgnlValueStack.setRoot()
|
||||
StrutsContext ognlContext = new StrutsContext(securityMemberAccess, accessor,
|
||||
new OgnlTypeConverterWrapper(xworkConverter));
|
||||
ognlContext.withRoot(this.root);
|
||||
|
||||
// OgnlReflectionContextFactory — already @Deprecated(forRemoval=true) since 6.8.0
|
||||
// Keep using Ognl.createDefaultContext(root) with raw type, or remove entirely
|
||||
```
|
||||
|
||||
### Tiles Plugin
|
||||
|
||||
The tiles plugin accessors operate on tiles-specific objects, not on `StrutsContext` directly. Options:
|
||||
- Parameterize with raw `OgnlContext` (use `PropertyAccessor<OgnlContext>`) if OGNL allows it
|
||||
- Use wildcard `PropertyAccessor<?>` if supported
|
||||
- Parameterize with `StrutsContext` if tiles always runs within a Struts context
|
||||
|
||||
Decision: determine during implementation based on what compiles cleanly.
|
||||
|
||||
### XWorkTypeConverterWrapper
|
||||
|
||||
Currently casts `Map` context to `OgnlContext`. After upgrade, `ognl.TypeConverter<StrutsContext>` passes `StrutsContext` directly — the cast goes away. Struts' own `TypeConverter` interface (in `conversion` package) may also need its `convertValue` signature updated.
|
||||
|
||||
### ReflectionContextState
|
||||
|
||||
Initially unchanged — continues to work via `Map<String, Object>` interface that `StrutsContext` inherits from `OgnlContext`. Promoting to typed fields is a follow-up.
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1: Version bump + StrutsContext + generics (this effort)
|
||||
|
||||
1. Bump `ognl.version` to `3.5.0-BETA4` in root `pom.xml`
|
||||
2. Create `StrutsContext extends OgnlContext<StrutsContext>` (constructor only)
|
||||
3. Update all OGNL interface implementations with `<StrutsContext>` type parameter (~20 classes in core)
|
||||
4. Update method signatures: `OgnlContext` → `StrutsContext` in all accessor/handler implementations
|
||||
5. Replace `Ognl.createDefaultContext()` with direct `StrutsContext` construction (3 call sites)
|
||||
6. Update tiles plugin accessor classes (~6-8 files)
|
||||
7. Update test files (~50+ files referencing `OgnlContext`)
|
||||
8. Verify all tests pass
|
||||
|
||||
### Phase 2: Typed context fields (follow-up)
|
||||
|
||||
- Promote `ReflectionContextState` map entries to `StrutsContext` typed fields
|
||||
- Update accessors to use typed getters instead of `context.get("string.key")`
|
||||
- Deprecate `ReflectionContextState` static methods
|
||||
|
||||
### Phase 3: Sandbox features (future, requires OGNL changes)
|
||||
|
||||
- Instance-based `OgnlRuntime` (OGNL-side work)
|
||||
- Per-sandbox accessor registrations
|
||||
- Isolated evaluation engines
|
||||
|
||||
## Risk Areas
|
||||
|
||||
### OgnlRuntime global statics
|
||||
|
||||
`OgnlRuntime.setPropertyAccessor(Class<?>, PropertyAccessor<C>)` is generic but the registration is global. Registering `PropertyAccessor<StrutsContext>` may cause unchecked warnings or issues when OGNL internally retrieves accessors with a different context type. May need raw types at registration boundary.
|
||||
|
||||
### OGNL internal context preservation
|
||||
|
||||
If OGNL internally creates new `OgnlContext` instances during expression evaluation (rather than preserving the passed-in `StrutsContext`), typed fields would be lost. BETA1 addressed "context root preservation during nested evaluations" but this needs runtime verification.
|
||||
|
||||
### Tiles plugin type compatibility
|
||||
|
||||
Tiles accessors may not naturally fit `StrutsContext` parameterization. Need to determine the right generic type during implementation.
|
||||
|
||||
### OGNL BETA stability
|
||||
|
||||
OGNL 3.5.0 is still in BETA. API changes may occur in subsequent releases. This is acceptable given the user is an OGNL contributor and can influence the API.
|
||||
|
||||
## Expected Outcomes
|
||||
|
||||
- Struts compiles and all tests pass against OGNL 3.5.0-BETA4
|
||||
- `StrutsContext` exists as the framework's OGNL context class
|
||||
- All OGNL interface implementations are properly parameterized with `<StrutsContext>`
|
||||
- Foundation is in place for typed context fields and eventual sandbox isolation
|
||||
- Any OGNL API issues discovered are reported/fixed upstream
|
||||
+6
-7
@@ -28,7 +28,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.MethodBeforeAdvice;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.ognl.StrutsContext;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -43,7 +43,7 @@ public class SecurityMemberAccessProxyTest extends XWorkJUnit4TestCase {
|
||||
private static final String PROXY_MEMBER_METHOD = "isExposeProxy";
|
||||
private static final String TEST_SUB_BEAN_CLASS_METHOD = "getIssueId";
|
||||
|
||||
private OgnlContext context;
|
||||
private StrutsContext context;
|
||||
private ActionProxy proxy;
|
||||
private SecurityMemberAccess sma;
|
||||
private ProxyService proxyService;
|
||||
@@ -57,14 +57,13 @@ public class SecurityMemberAccessProxyTest extends XWorkJUnit4TestCase {
|
||||
XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("org/apache/struts2/spring/actionContext-xwork.xml");
|
||||
loadConfigurationProviders(provider);
|
||||
|
||||
context = ognl.Ognl.createDefaultContext(null);
|
||||
proxy = actionProxyFactory.createActionProxy(null, "chaintoAOPedTestSubBeanAction", null, context);
|
||||
proxyObjectProxyMember = proxy.getAction().getClass().getMethod(PROXY_MEMBER_METHOD);
|
||||
proxyObjectNonProxyMember = proxy.getAction().getClass().getMethod(TEST_SUB_BEAN_CLASS_METHOD);
|
||||
|
||||
proxyService = new StrutsProxyService(new StrutsProxyCacheFactory<>("1000", "basic"));
|
||||
sma = new SecurityMemberAccess(null, null);
|
||||
sma.setProxyService(proxyService);
|
||||
context = new StrutsContext(sma);
|
||||
proxy = actionProxyFactory.createActionProxy(null, "chaintoAOPedTestSubBeanAction", null, context);
|
||||
proxyObjectProxyMember = proxy.getAction().getClass().getMethod(PROXY_MEMBER_METHOD);
|
||||
proxyObjectNonProxyMember = proxy.getAction().getClass().getMethod(TEST_SUB_BEAN_CLASS_METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Accesses attributes in any scope.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class AnyScopePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,6 +29,7 @@ import ognl.PropertyAccessor;
|
||||
* @param <T> The type of the accessed root object.
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class DelegatePropertyAccessor<T> implements PropertyAccessor {
|
||||
|
||||
/**
|
||||
|
||||
+1
@@ -29,6 +29,7 @@ import ognl.PropertyAccessor;
|
||||
* @param <T> The root object type from which the target object will be extracted.
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class NestedObjectDelegatePropertyAccessor<T> implements PropertyAccessor {
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ import ognl.PropertyAccessor;
|
||||
* @param <T> The type of the root object to evaluate.
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public interface PropertyAccessorDelegateFactory<T> {
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.apache.tiles.request.Request;
|
||||
/**
|
||||
* Accesses a scope.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ScopePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
<jaxb-impl.version>4.0.7</jaxb-impl.version>
|
||||
<log4j2.version>2.25.4</log4j2.version>
|
||||
<mockito.version>5.23.0</mockito.version>
|
||||
<ognl.version>3.4.10</ognl.version>
|
||||
<ognl.version>3.5.0-BETA4</ognl.version>
|
||||
<slf4j.version>2.0.17</slf4j.version>
|
||||
<spring.version>6.2.12</spring.version>
|
||||
<struts-annotations.version>2.0</struts-annotations.version>
|
||||
@@ -314,6 +314,7 @@
|
||||
<exclude>src/main/webapp/fonts/**/*</exclude>
|
||||
<exclude>thoughts/**/*.md</exclude>
|
||||
<exclude>test-output/**</exclude>
|
||||
<exclude>docs/**</exclude>
|
||||
</inputExcludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
Reference in New Issue
Block a user