|
|
fc09ee |
From fd220124926ff0c6e760d5331eb62e09b20b46a9 Mon Sep 17 00:00:00 2001
|
|
|
fc09ee |
From: Marian Koncek <mkoncek@redhat.com>
|
|
|
fc09ee |
Date: Wed, 18 Dec 2019 16:01:16 +0100
|
|
|
fc09ee |
Subject: [PATCH] CVE-2014-0114
|
|
|
fc09ee |
|
|
|
fc09ee |
---
|
|
|
fc09ee |
.../beanutils/BeanIntrospectionData.java | 154 +++++++++++++
|
|
|
fc09ee |
.../commons/beanutils/BeanIntrospector.java | 53 +++++
|
|
|
fc09ee |
.../beanutils/DefaultBeanIntrospector.java | 181 +++++++++++++++
|
|
|
fc09ee |
.../DefaultIntrospectionContext.java | 108 +++++++++
|
|
|
fc09ee |
.../beanutils/IntrospectionContext.java | 99 ++++++++
|
|
|
fc09ee |
.../commons/beanutils/PropertyUtils.java | 15 ++
|
|
|
fc09ee |
.../commons/beanutils/PropertyUtilsBean.java | 213 +++++++++---------
|
|
|
fc09ee |
.../SuppressPropertiesBeanIntrospector.java | 75 ++++++
|
|
|
fc09ee |
...essPropertiesBeanIntrospectorTestCase.java | 127 +++++++++++
|
|
|
fc09ee |
.../beanutils/bugs/Jira463TestCase.java | 48 ++++
|
|
|
fc09ee |
.../beanutils/bugs/Jira520TestCase.java | 39 ++++
|
|
|
fc09ee |
11 files changed, 1000 insertions(+), 112 deletions(-)
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/BeanIntrospector.java
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/IntrospectionContext.java
|
|
|
fc09ee |
create mode 100644 src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
|
|
|
fc09ee |
create mode 100644 src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java
|
|
|
fc09ee |
create mode 100644 src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java
|
|
|
fc09ee |
create mode 100644 src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java
|
|
|
fc09ee |
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java b/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..1ab1d1e
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/BeanIntrospectionData.java
|
|
|
fc09ee |
@@ -0,0 +1,154 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.IntrospectionException;
|
|
|
fc09ee |
+import java.beans.PropertyDescriptor;
|
|
|
fc09ee |
+import java.lang.reflect.Method;
|
|
|
fc09ee |
+import java.util.HashMap;
|
|
|
fc09ee |
+import java.util.Map;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * An internally used helper class for storing introspection information about a bean
|
|
|
fc09ee |
+ * class.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * This class is used by {@link PropertyUtilsBean}. When accessing bean properties via
|
|
|
fc09ee |
+ * reflection information about the properties available and their types and access
|
|
|
fc09ee |
+ * methods must be present. {@code PropertyUtilsBean} stores this information in a cache
|
|
|
fc09ee |
+ * so that it can be accessed quickly. The cache stores instances of this class.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * This class mainly stores information about the properties of a bean class. Per default,
|
|
|
fc09ee |
+ * this is contained in {@code PropertyDescriptor} objects. Some additional information
|
|
|
fc09ee |
+ * required by the {@code BeanUtils} library is also stored here.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ * @since 1.9.1
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+class BeanIntrospectionData {
|
|
|
fc09ee |
+ /** An array with property descriptors for the managed bean class. */
|
|
|
fc09ee |
+ private final PropertyDescriptor[] descriptors;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** A map for remembering the write method names for properties. */
|
|
|
fc09ee |
+ private final Map writeMethodNames;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Creates a new instance of {@code BeanIntrospectionData} and initializes its
|
|
|
fc09ee |
+ * completely.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param descs the array with the descriptors of the available properties
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public BeanIntrospectionData(final PropertyDescriptor[] descs) {
|
|
|
fc09ee |
+ this(descs, setUpWriteMethodNames(descs));
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Creates a new instance of {@code BeanIntrospectionData} and allows setting the map
|
|
|
fc09ee |
+ * with write method names. This constructor is mainly used for testing purposes.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param descs the array with the descriptors of the available properties
|
|
|
fc09ee |
+ * @param writeMethNames the map with the names of write methods
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ BeanIntrospectionData(final PropertyDescriptor[] descs, final Map writeMethNames) {
|
|
|
fc09ee |
+ descriptors = descs;
|
|
|
fc09ee |
+ writeMethodNames = writeMethNames;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the array with property descriptors.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return the property descriptors for the associated bean class
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public PropertyDescriptor[] getDescriptors() {
|
|
|
fc09ee |
+ return descriptors;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the {@code PropertyDescriptor} for the property with the specified name. If
|
|
|
fc09ee |
+ * this property is unknown, result is null.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param name the name of the property in question
|
|
|
fc09ee |
+ * @return the {@code PropertyDescriptor} for this property or null
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public PropertyDescriptor getDescriptor(final String name) {
|
|
|
fc09ee |
+ PropertyDescriptor[] descriptors = getDescriptors();
|
|
|
fc09ee |
+ for (int i = 0; i < descriptors.length; ++i) {
|
|
|
fc09ee |
+ PropertyDescriptor pd = descriptors[i];
|
|
|
fc09ee |
+ if (name.equals(pd.getName())) {
|
|
|
fc09ee |
+ return pd;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ return null;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the write method for the property determined by the given
|
|
|
fc09ee |
+ * {@code PropertyDescriptor}. This information is normally available in the
|
|
|
fc09ee |
+ * descriptor object itself. However, at least by the ORACLE implementation, the
|
|
|
fc09ee |
+ * method is stored as a {@code SoftReference}. If this reference has been freed by
|
|
|
fc09ee |
+ * the GC, it may be the case that the method cannot be obtained again. Then,
|
|
|
fc09ee |
+ * additional information stored in this object is necessary to obtain the method
|
|
|
fc09ee |
+ * again.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param beanCls the class of the affected bean
|
|
|
fc09ee |
+ * @param desc the {@code PropertyDescriptor} of the desired property
|
|
|
fc09ee |
+ * @return the write method for this property or null if there is none
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public Method getWriteMethod(final Class beanCls, final PropertyDescriptor desc) {
|
|
|
fc09ee |
+ Method method = desc.getWriteMethod();
|
|
|
fc09ee |
+ if (method == null) {
|
|
|
fc09ee |
+ final String methodName = (String) writeMethodNames.get(desc.getName());
|
|
|
fc09ee |
+ if (methodName != null) {
|
|
|
fc09ee |
+ method = MethodUtils.getAccessibleMethod(beanCls, methodName,
|
|
|
fc09ee |
+ desc.getPropertyType());
|
|
|
fc09ee |
+ if (method != null) {
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ desc.setWriteMethod(method);
|
|
|
fc09ee |
+ } catch (final IntrospectionException e) {
|
|
|
fc09ee |
+ // ignore, in this case the method is not cached
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ return method;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Initializes the map with the names of the write methods for the supported
|
|
|
fc09ee |
+ * properties. The method names - if defined - need to be stored separately because
|
|
|
fc09ee |
+ * they may get lost when the GC claims soft references used by the
|
|
|
fc09ee |
+ * {@code PropertyDescriptor} objects.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param descs the array with the descriptors of the available properties
|
|
|
fc09ee |
+ * @return the map with the names of write methods for properties
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private static Map setUpWriteMethodNames(final PropertyDescriptor[] descs) {
|
|
|
fc09ee |
+ final Map methods = new HashMap();
|
|
|
fc09ee |
+ for (int i = 0; i < descs.length; ++i) {
|
|
|
fc09ee |
+ PropertyDescriptor pd = descs[i];
|
|
|
fc09ee |
+ final Method method = pd.getWriteMethod();
|
|
|
fc09ee |
+ if (method != null) {
|
|
|
fc09ee |
+ methods.put(pd.getName(), method.getName());
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ return methods;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..3f5545e
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/BeanIntrospector.java
|
|
|
fc09ee |
@@ -0,0 +1,53 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.IntrospectionException;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Definition of an interface for components that can perform introspection on
|
|
|
fc09ee |
+ * bean classes.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Before {@link PropertyUtils} can be used for interaction with a specific Java
|
|
|
fc09ee |
+ * class, the class's properties have to be determined. This is called
|
|
|
fc09ee |
+ * introspection and is initiated automatically on demand.
|
|
|
fc09ee |
+ * PropertyUtils does not perform introspection on its own, but
|
|
|
fc09ee |
+ * delegates this task to one or more objects implementing this interface. This
|
|
|
fc09ee |
+ * makes it possible to customize introspection which may be useful for certain
|
|
|
fc09ee |
+ * code bases using non-standard conventions for accessing properties.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id: BeanIntrospector.java 1540359 2013-11-09 18:10:52Z oheger $
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public interface BeanIntrospector {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Performs introspection on a Java class. The current class to be inspected
|
|
|
fc09ee |
+ * can be queried from the passed in IntrospectionContext
|
|
|
fc09ee |
+ * object. A typical implementation has to obtain this class, determine its
|
|
|
fc09ee |
+ * properties according to the rules it implements, and add them to the
|
|
|
fc09ee |
+ * passed in context object.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param icontext the context object for interaction with the initiator of
|
|
|
fc09ee |
+ * the introspection request
|
|
|
fc09ee |
+ * @throws IntrospectionException if an error occurs during introspection
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ void introspect(IntrospectionContext icontext)
|
|
|
fc09ee |
+ throws IntrospectionException;
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..60657d0
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/DefaultBeanIntrospector.java
|
|
|
fc09ee |
@@ -0,0 +1,181 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.BeanInfo;
|
|
|
fc09ee |
+import java.beans.IndexedPropertyDescriptor;
|
|
|
fc09ee |
+import java.beans.IntrospectionException;
|
|
|
fc09ee |
+import java.beans.Introspector;
|
|
|
fc09ee |
+import java.beans.PropertyDescriptor;
|
|
|
fc09ee |
+import java.lang.reflect.Method;
|
|
|
fc09ee |
+import java.util.List;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import org.apache.commons.logging.Log;
|
|
|
fc09ee |
+import org.apache.commons.logging.LogFactory;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * The default {@link BeanIntrospector} implementation.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * This class implements a default bean introspection algorithm based on the JDK
|
|
|
fc09ee |
+ * classes in the java.beans package. It discovers properties
|
|
|
fc09ee |
+ * conforming to the Java Beans specification.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * This class is a singleton. The single instance can be obtained using the
|
|
|
fc09ee |
+ * {@code INSTANCE} field. It does not define any state and thus can be
|
|
|
fc09ee |
+ * shared by arbitrary clients. {@link PropertyUtils} per default uses this
|
|
|
fc09ee |
+ * instance as its only {@code BeanIntrospector} object.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public class DefaultBeanIntrospector implements BeanIntrospector {
|
|
|
fc09ee |
+ /** The singleton instance of this class. */
|
|
|
fc09ee |
+ public static final BeanIntrospector INSTANCE = new DefaultBeanIntrospector();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** Constant for argument types of a method that expects no arguments. */
|
|
|
fc09ee |
+ private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** Constant for arguments types of a method that expects a list argument. */
|
|
|
fc09ee |
+ private static final Class[] LIST_CLASS_PARAMETER = new Class[] { java.util.List.class };
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** Log instance */
|
|
|
fc09ee |
+ private final Log log = LogFactory.getLog(getClass());
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Private constructor so that no instances can be created.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private DefaultBeanIntrospector() {
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Performs introspection of a specific Java class. This implementation uses
|
|
|
fc09ee |
+ * the {@code java.beans.Introspector.getBeanInfo()} method to obtain
|
|
|
fc09ee |
+ * all property descriptors for the current class and adds them to the
|
|
|
fc09ee |
+ * passed in introspection context.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param icontext the introspection context
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void introspect(final IntrospectionContext icontext) {
|
|
|
fc09ee |
+ BeanInfo beanInfo = null;
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
|
|
|
fc09ee |
+ } catch (final IntrospectionException e) {
|
|
|
fc09ee |
+ // no descriptors are added to the context
|
|
|
fc09ee |
+ log.error(
|
|
|
fc09ee |
+ "Error when inspecting class " + icontext.getTargetClass(),
|
|
|
fc09ee |
+ e);
|
|
|
fc09ee |
+ return;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
|
|
|
fc09ee |
+ if (descriptors == null) {
|
|
|
fc09ee |
+ descriptors = new PropertyDescriptor[0];
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ handleIndexedPropertyDescriptors(icontext.getTargetClass(),
|
|
|
fc09ee |
+ descriptors);
|
|
|
fc09ee |
+ icontext.addPropertyDescriptors(descriptors);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * This method fixes an issue where IndexedPropertyDescriptor behaves
|
|
|
fc09ee |
+ * differently in different versions of the JDK for 'indexed' properties
|
|
|
fc09ee |
+ * which use java.util.List (rather than an array). It implements a
|
|
|
fc09ee |
+ * workaround for Bug 28358. If you have a Bean with the following
|
|
|
fc09ee |
+ * getters/setters for an indexed property:
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * public List getFoo()
|
|
|
fc09ee |
+ * public Object getFoo(int index)
|
|
|
fc09ee |
+ * public void setFoo(List foo)
|
|
|
fc09ee |
+ * public void setFoo(int index, Object foo)
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod()
|
|
|
fc09ee |
+ * behave as follows:
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * JDK 1.3.1_04: returns valid Method objects from these methods.
|
|
|
fc09ee |
+ * JDK 1.4.2_05: returns null from these methods.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param beanClass the current class to be inspected
|
|
|
fc09ee |
+ * @param descriptors the array with property descriptors
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private void handleIndexedPropertyDescriptors(final Class beanClass,
|
|
|
fc09ee |
+ final PropertyDescriptor[] descriptors) {
|
|
|
fc09ee |
+ for (final PropertyDescriptor pd : descriptors) {
|
|
|
fc09ee |
+ if (pd instanceof IndexedPropertyDescriptor) {
|
|
|
fc09ee |
+ final IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor) pd;
|
|
|
fc09ee |
+ final String propName = descriptor.getName().substring(0, 1)
|
|
|
fc09ee |
+ .toUpperCase()
|
|
|
fc09ee |
+ + descriptor.getName().substring(1);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ if (descriptor.getReadMethod() == null) {
|
|
|
fc09ee |
+ final String methodName = descriptor.getIndexedReadMethod() != null ? descriptor
|
|
|
fc09ee |
+ .getIndexedReadMethod().getName() : "get"
|
|
|
fc09ee |
+ + propName;
|
|
|
fc09ee |
+ final Method readMethod = MethodUtils
|
|
|
fc09ee |
+ .getMatchingAccessibleMethod(beanClass, methodName,
|
|
|
fc09ee |
+ EMPTY_CLASS_PARAMETERS);
|
|
|
fc09ee |
+ if (readMethod != null) {
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ descriptor.setReadMethod(readMethod);
|
|
|
fc09ee |
+ } catch (final Exception e) {
|
|
|
fc09ee |
+ log.error(
|
|
|
fc09ee |
+ "Error setting indexed property read method",
|
|
|
fc09ee |
+ e);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ if (descriptor.getWriteMethod() == null) {
|
|
|
fc09ee |
+ final String methodName = descriptor.getIndexedWriteMethod() != null ? descriptor
|
|
|
fc09ee |
+ .getIndexedWriteMethod().getName() : "set"
|
|
|
fc09ee |
+ + propName;
|
|
|
fc09ee |
+ Method writeMethod = MethodUtils
|
|
|
fc09ee |
+ .getMatchingAccessibleMethod(beanClass, methodName,
|
|
|
fc09ee |
+ LIST_CLASS_PARAMETER);
|
|
|
fc09ee |
+ if (writeMethod == null) {
|
|
|
fc09ee |
+ for (final Method m : beanClass.getMethods()) {
|
|
|
fc09ee |
+ if (m.getName().equals(methodName)) {
|
|
|
fc09ee |
+ final Class[] parameterTypes = m.getParameterTypes();
|
|
|
fc09ee |
+ if (parameterTypes.length == 1
|
|
|
fc09ee |
+ && List.class
|
|
|
fc09ee |
+ .isAssignableFrom(parameterTypes[0])) {
|
|
|
fc09ee |
+ writeMethod = m;
|
|
|
fc09ee |
+ break;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ if (writeMethod != null) {
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ descriptor.setWriteMethod(writeMethod);
|
|
|
fc09ee |
+ } catch (final Exception e) {
|
|
|
fc09ee |
+ log.error(
|
|
|
fc09ee |
+ "Error setting indexed property write method",
|
|
|
fc09ee |
+ e);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java b/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..9171198
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/DefaultIntrospectionContext.java
|
|
|
fc09ee |
@@ -0,0 +1,108 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.PropertyDescriptor;
|
|
|
fc09ee |
+import java.util.HashMap;
|
|
|
fc09ee |
+import java.util.Map;
|
|
|
fc09ee |
+import java.util.Set;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * An implementation of the {@code IntrospectionContext} interface used by
|
|
|
fc09ee |
+ * {@link PropertyUtilsBean} when doing introspection of a bean class.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * This class implements the methods required by the
|
|
|
fc09ee |
+ * {@code IntrospectionContext} interface in a straight-forward manner
|
|
|
fc09ee |
+ * based on a map. It is used internally only. It is not thread-safe.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+class DefaultIntrospectionContext implements IntrospectionContext {
|
|
|
fc09ee |
+ /** Constant for an empty array of property descriptors. */
|
|
|
fc09ee |
+ private static final PropertyDescriptor[] EMPTY_DESCRIPTORS = new PropertyDescriptor[0];
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** The current class for introspection. */
|
|
|
fc09ee |
+ private final Class currentClass;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** A map for storing the already added property descriptors. */
|
|
|
fc09ee |
+ private final Map descriptors;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Creates a new instance of DefaultIntrospectionContext and sets
|
|
|
fc09ee |
+ * the current class for introspection.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param cls the current class
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public DefaultIntrospectionContext(final Class cls) {
|
|
|
fc09ee |
+ currentClass = cls;
|
|
|
fc09ee |
+ descriptors = new HashMap();
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public Class getTargetClass() {
|
|
|
fc09ee |
+ return currentClass;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void addPropertyDescriptor(final PropertyDescriptor desc) {
|
|
|
fc09ee |
+ if (desc == null) {
|
|
|
fc09ee |
+ throw new IllegalArgumentException(
|
|
|
fc09ee |
+ "Property descriptor must not be null!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ descriptors.put(desc.getName(), desc);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void addPropertyDescriptors(final PropertyDescriptor[] descs) {
|
|
|
fc09ee |
+ if (descs == null) {
|
|
|
fc09ee |
+ throw new IllegalArgumentException(
|
|
|
fc09ee |
+ "Array with descriptors must not be null!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ for (int i = 0; i < descs.length; ++i) {
|
|
|
fc09ee |
+ addPropertyDescriptor(descs[i]);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public boolean hasProperty(final String name) {
|
|
|
fc09ee |
+ return descriptors.containsKey(name);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public PropertyDescriptor getPropertyDescriptor(final String name) {
|
|
|
fc09ee |
+ return (PropertyDescriptor) descriptors.get(name);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void removePropertyDescriptor(final String name) {
|
|
|
fc09ee |
+ descriptors.remove(name);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public Set propertyNames() {
|
|
|
fc09ee |
+ return descriptors.keySet();
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns an array with all descriptors added to this context. This method
|
|
|
fc09ee |
+ * is used to obtain the results of introspection.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return an array with all known property descriptors
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public PropertyDescriptor[] getPropertyDescriptors() {
|
|
|
fc09ee |
+ return (PropertyDescriptor[]) descriptors.values().toArray(EMPTY_DESCRIPTORS);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java b/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..c48f186
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/IntrospectionContext.java
|
|
|
fc09ee |
@@ -0,0 +1,99 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.PropertyDescriptor;
|
|
|
fc09ee |
+import java.util.Set;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * A context interface used during introspection for querying and setting
|
|
|
fc09ee |
+ * property descriptors.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * An implementation of this interface is passed to {@link BeanIntrospector}
|
|
|
fc09ee |
+ * objects during processing of a bean class. It allows the
|
|
|
fc09ee |
+ * {@code BeanIntrospector} to deliver descriptors for properties it has
|
|
|
fc09ee |
+ * detected. It is also possible to find out which properties have already been
|
|
|
fc09ee |
+ * found by another {@code BeanIntrospector}; this allows multiple
|
|
|
fc09ee |
+ * {@code BeanIntrospector} instances to collaborate.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id: IntrospectionContext.java 1540359 2013-11-09 18:10:52Z oheger $
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public interface IntrospectionContext {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the class that is subject of introspection.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return the current class
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ Class getTargetClass();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Adds the given property descriptor to this context. This method is called
|
|
|
fc09ee |
+ * by a {@code BeanIntrospector} during introspection for each detected
|
|
|
fc09ee |
+ * property. If this context already contains a descriptor for the affected
|
|
|
fc09ee |
+ * property, it is overridden.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param desc the property descriptor
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ void addPropertyDescriptor(PropertyDescriptor desc);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Adds an array of property descriptors to this context. Using this method
|
|
|
fc09ee |
+ * multiple descriptors can be added at once.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param descriptors the array of descriptors to be added
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ void addPropertyDescriptors(PropertyDescriptor[] descriptors);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tests whether a descriptor for the property with the given name is
|
|
|
fc09ee |
+ * already contained in this context. This method can be used for instance
|
|
|
fc09ee |
+ * to prevent that an already existing property descriptor is overridden.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param name the name of the property in question
|
|
|
fc09ee |
+ * @return true if a descriptor for this property has already been
|
|
|
fc09ee |
+ * added, false otherwise
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ boolean hasProperty(String name);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the descriptor for the property with the given name or
|
|
|
fc09ee |
+ * null if this property is unknown.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param name the name of the property in question
|
|
|
fc09ee |
+ * @return the descriptor for this property or null if this property
|
|
|
fc09ee |
+ * is unknown
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ PropertyDescriptor getPropertyDescriptor(String name);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Removes the descriptor for the property with the given name.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param name the name of the affected property
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ void removePropertyDescriptor(String name);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns a set with the names of all properties known to this context.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return a set with the known property names
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ Set propertyNames();
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/PropertyUtils.java b/src/main/java/org/apache/commons/beanutils/PropertyUtils.java
|
|
|
fc09ee |
index 6344b4f..b3ce0e6 100644
|
|
|
fc09ee |
--- a/src/main/java/org/apache/commons/beanutils/PropertyUtils.java
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/PropertyUtils.java
|
|
|
fc09ee |
@@ -890,5 +890,20 @@ public class PropertyUtils {
|
|
|
fc09ee |
PropertyUtilsBean.getInstance().setSimpleProperty(bean, name, value);
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
+ public static void addBeanIntrospector(final BeanIntrospector introspector) {
|
|
|
fc09ee |
+ PropertyUtilsBean.getInstance().addBeanIntrospector(introspector);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Removes the specified BeanIntrospector .
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param introspector the BeanIntrospector to be removed
|
|
|
fc09ee |
+ * @return true if the BeanIntrospector existed and
|
|
|
fc09ee |
+ * could be removed, false otherwise
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public static boolean removeBeanIntrospector(final BeanIntrospector introspector) {
|
|
|
fc09ee |
+ return PropertyUtilsBean.getInstance().removeBeanIntrospector(
|
|
|
fc09ee |
+ introspector);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
|
|
|
fc09ee |
index 1894f21..c7817cd 100644
|
|
|
fc09ee |
--- a/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/PropertyUtilsBean.java
|
|
|
fc09ee |
@@ -37,6 +37,7 @@ import org.apache.commons.collections.FastHashMap;
|
|
|
fc09ee |
import org.apache.commons.logging.Log;
|
|
|
fc09ee |
import org.apache.commons.logging.LogFactory;
|
|
|
fc09ee |
|
|
|
fc09ee |
+import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
fc09ee |
|
|
|
fc09ee |
/**
|
|
|
fc09ee |
* Utility methods for using Java Reflection APIs to facilitate generic
|
|
|
fc09ee |
@@ -130,7 +131,9 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
|
|
|
fc09ee |
/** Log instance */
|
|
|
fc09ee |
private Log log = LogFactory.getLog(PropertyUtils.class);
|
|
|
fc09ee |
-
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** The list with BeanIntrospector objects. */
|
|
|
fc09ee |
+ private final List introspectors;
|
|
|
fc09ee |
// ---------------------------------------------------------- Constructors
|
|
|
fc09ee |
|
|
|
fc09ee |
/** Base constructor */
|
|
|
fc09ee |
@@ -139,6 +142,8 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
descriptorsCache.setFast(true);
|
|
|
fc09ee |
mappedDescriptorsCache = new WeakFastHashMap();
|
|
|
fc09ee |
mappedDescriptorsCache.setFast(true);
|
|
|
fc09ee |
+ introspectors = new CopyOnWriteArrayList();
|
|
|
fc09ee |
+ resetBeanIntrospectors();
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
|
|
|
fc09ee |
@@ -183,6 +188,47 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
}
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Resets the {@link BeanIntrospector} objects registered at this instance. After this
|
|
|
fc09ee |
+ * method was called, only the default {@code BeanIntrospector} is registered.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public final void resetBeanIntrospectors() {
|
|
|
fc09ee |
+ introspectors.clear();
|
|
|
fc09ee |
+ introspectors.add(DefaultBeanIntrospector.INSTANCE);
|
|
|
fc09ee |
+ introspectors.add(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Adds a BeanIntrospector . This object is invoked when the
|
|
|
fc09ee |
+ * property descriptors of a class need to be obtained.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param introspector the BeanIntrospector to be added (must
|
|
|
fc09ee |
+ * not be null
|
|
|
fc09ee |
+ * @throws IllegalArgumentException if the argument is null
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void addBeanIntrospector(final BeanIntrospector introspector) {
|
|
|
fc09ee |
+ if (introspector == null) {
|
|
|
fc09ee |
+ throw new IllegalArgumentException(
|
|
|
fc09ee |
+ "BeanIntrospector must not be null!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ introspectors.add(introspector);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Removes the specified BeanIntrospector .
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param introspector the BeanIntrospector to be removed
|
|
|
fc09ee |
+ * @return true if the BeanIntrospector existed and
|
|
|
fc09ee |
+ * could be removed, false otherwise
|
|
|
fc09ee |
+ * @since 1.9
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public boolean removeBeanIntrospector(final BeanIntrospector introspector) {
|
|
|
fc09ee |
+ return introspectors.remove(introspector);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
/**
|
|
|
fc09ee |
* Clear any cached property descriptors information for all classes
|
|
|
fc09ee |
* loaded by any class loaders. This is useful in cases where class
|
|
|
fc09ee |
@@ -909,17 +955,12 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
return (null);
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
- PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
|
|
|
fc09ee |
- if (descriptors != null) {
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- for (int i = 0; i < descriptors.length; i++) {
|
|
|
fc09ee |
- if (name.equals(descriptors[i].getName())) {
|
|
|
fc09ee |
- return (descriptors[i]);
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
+ final BeanIntrospectionData data = getIntrospectionData(bean.getClass());
|
|
|
fc09ee |
+ PropertyDescriptor result = data.getDescriptor(name);
|
|
|
fc09ee |
+ if (result != null) {
|
|
|
fc09ee |
+ return result;
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
- PropertyDescriptor result = null;
|
|
|
fc09ee |
FastHashMap mappedDescriptors =
|
|
|
fc09ee |
getMappedPropertyDescriptors(bean);
|
|
|
fc09ee |
if (mappedDescriptors == null) {
|
|
|
fc09ee |
@@ -960,110 +1001,10 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
* @exception IllegalArgumentException if beanClass is null
|
|
|
fc09ee |
*/
|
|
|
fc09ee |
public PropertyDescriptor[]
|
|
|
fc09ee |
- getPropertyDescriptors(Class beanClass) {
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- if (beanClass == null) {
|
|
|
fc09ee |
- throw new IllegalArgumentException("No bean class specified");
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- // Look up any cached descriptors for this bean class
|
|
|
fc09ee |
- PropertyDescriptor[] descriptors = null;
|
|
|
fc09ee |
- descriptors =
|
|
|
fc09ee |
- (PropertyDescriptor[]) descriptorsCache.get(beanClass);
|
|
|
fc09ee |
- if (descriptors != null) {
|
|
|
fc09ee |
- return (descriptors);
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- // Introspect the bean and cache the generated descriptors
|
|
|
fc09ee |
- BeanInfo beanInfo = null;
|
|
|
fc09ee |
- try {
|
|
|
fc09ee |
- beanInfo = Introspector.getBeanInfo(beanClass);
|
|
|
fc09ee |
- } catch (IntrospectionException e) {
|
|
|
fc09ee |
- return (new PropertyDescriptor[0]);
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- descriptors = beanInfo.getPropertyDescriptors();
|
|
|
fc09ee |
- if (descriptors == null) {
|
|
|
fc09ee |
- descriptors = new PropertyDescriptor[0];
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- // ----------------- Workaround for Bug 28358 --------- START ------------------
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- // The following code fixes an issue where IndexedPropertyDescriptor behaves
|
|
|
fc09ee |
- // Differently in different versions of the JDK for 'indexed' properties which
|
|
|
fc09ee |
- // use java.util.List (rather than an array).
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- // If you have a Bean with the following getters/setters for an indexed property:
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- // public List getFoo()
|
|
|
fc09ee |
- // public Object getFoo(int index)
|
|
|
fc09ee |
- // public void setFoo(List foo)
|
|
|
fc09ee |
- // public void setFoo(int index, Object foo)
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- // then the IndexedPropertyDescriptor's getReadMethod() and getWriteMethod()
|
|
|
fc09ee |
- // behave as follows:
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- // JDK 1.3.1_04: returns valid Method objects from these methods.
|
|
|
fc09ee |
- // JDK 1.4.2_05: returns null from these methods.
|
|
|
fc09ee |
- //
|
|
|
fc09ee |
- for (int i = 0; i < descriptors.length; i++) {
|
|
|
fc09ee |
- if (descriptors[i] instanceof IndexedPropertyDescriptor) {
|
|
|
fc09ee |
- IndexedPropertyDescriptor descriptor = (IndexedPropertyDescriptor)descriptors[i];
|
|
|
fc09ee |
- String propName = descriptor.getName().substring(0, 1).toUpperCase() +
|
|
|
fc09ee |
- descriptor.getName().substring(1);
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- if (descriptor.getReadMethod() == null) {
|
|
|
fc09ee |
- String methodName = descriptor.getIndexedReadMethod() != null
|
|
|
fc09ee |
- ? descriptor.getIndexedReadMethod().getName()
|
|
|
fc09ee |
- : "get" + propName;
|
|
|
fc09ee |
- Method readMethod = MethodUtils.getMatchingAccessibleMethod(beanClass,
|
|
|
fc09ee |
- methodName,
|
|
|
fc09ee |
- EMPTY_CLASS_PARAMETERS);
|
|
|
fc09ee |
- if (readMethod != null) {
|
|
|
fc09ee |
- try {
|
|
|
fc09ee |
- descriptor.setReadMethod(readMethod);
|
|
|
fc09ee |
- } catch(Exception e) {
|
|
|
fc09ee |
- log.error("Error setting indexed property read method", e);
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- if (descriptor.getWriteMethod() == null) {
|
|
|
fc09ee |
- String methodName = descriptor.getIndexedWriteMethod() != null
|
|
|
fc09ee |
- ? descriptor.getIndexedWriteMethod().getName()
|
|
|
fc09ee |
- : "set" + propName;
|
|
|
fc09ee |
- Method writeMethod = MethodUtils.getMatchingAccessibleMethod(beanClass,
|
|
|
fc09ee |
- methodName,
|
|
|
fc09ee |
- LIST_CLASS_PARAMETER);
|
|
|
fc09ee |
- if (writeMethod == null) {
|
|
|
fc09ee |
- Method[] methods = beanClass.getMethods();
|
|
|
fc09ee |
- for (int j = 0; j < methods.length; j++) {
|
|
|
fc09ee |
- if (methods[j].getName().equals(methodName)) {
|
|
|
fc09ee |
- Class[] parameterTypes = methods[j].getParameterTypes();
|
|
|
fc09ee |
- if (parameterTypes.length == 1 &&
|
|
|
fc09ee |
- List.class.isAssignableFrom(parameterTypes[0])) {
|
|
|
fc09ee |
- writeMethod = methods[j];
|
|
|
fc09ee |
- break;
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- if (writeMethod != null) {
|
|
|
fc09ee |
- try {
|
|
|
fc09ee |
- descriptor.setWriteMethod(writeMethod);
|
|
|
fc09ee |
- } catch(Exception e) {
|
|
|
fc09ee |
- log.error("Error setting indexed property write method", e);
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- }
|
|
|
fc09ee |
- // ----------------- Workaround for Bug 28358 ---------- END -------------------
|
|
|
fc09ee |
-
|
|
|
fc09ee |
- descriptorsCache.put(beanClass, descriptors);
|
|
|
fc09ee |
- return (descriptors);
|
|
|
fc09ee |
-
|
|
|
fc09ee |
+ getPropertyDescriptors(Class beanClass) {
|
|
|
fc09ee |
+ return getIntrospectionData(beanClass).getDescriptors();
|
|
|
fc09ee |
}
|
|
|
fc09ee |
|
|
|
fc09ee |
-
|
|
|
fc09ee |
/**
|
|
|
fc09ee |
* Retrieve the property descriptors for the specified bean,
|
|
|
fc09ee |
* introspecting and caching them the first time a particular bean class
|
|
|
fc09ee |
@@ -2248,4 +2189,52 @@ public class PropertyUtilsBean {
|
|
|
fc09ee |
|
|
|
fc09ee |
}
|
|
|
fc09ee |
}
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Obtains the {@code BeanIntrospectionData} object describing the specified bean
|
|
|
fc09ee |
+ * class. This object is looked up in the internal cache. If necessary, introspection
|
|
|
fc09ee |
+ * is performed now on the affected bean class, and the results object is created.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param beanClass the bean class in question
|
|
|
fc09ee |
+ * @return the {@code BeanIntrospectionData} object for this class
|
|
|
fc09ee |
+ * @throws IllegalArgumentException if the bean class is null
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private BeanIntrospectionData getIntrospectionData(final Class beanClass) {
|
|
|
fc09ee |
+ if (beanClass == null) {
|
|
|
fc09ee |
+ throw new IllegalArgumentException("No bean class specified");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ // Look up any cached information for this bean class
|
|
|
fc09ee |
+ BeanIntrospectionData data = (BeanIntrospectionData) descriptorsCache.get(beanClass);
|
|
|
fc09ee |
+ if (data == null) {
|
|
|
fc09ee |
+ data = fetchIntrospectionData(beanClass);
|
|
|
fc09ee |
+ descriptorsCache.put(beanClass, data);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ return data;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Performs introspection on the specified class. This method invokes all {@code BeanIntrospector} objects that were
|
|
|
fc09ee |
+ * added to this instance.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param beanClass the class to be inspected
|
|
|
fc09ee |
+ * @return a data object with the results of introspection
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private BeanIntrospectionData fetchIntrospectionData(final Class beanClass) {
|
|
|
fc09ee |
+ final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ Iterator it = introspectors.iterator();
|
|
|
fc09ee |
+ while (it.hasNext()) {
|
|
|
fc09ee |
+ final BeanIntrospector bi = (BeanIntrospector) it.next();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ bi.introspect(ictx);
|
|
|
fc09ee |
+ } catch (final IntrospectionException iex) {
|
|
|
fc09ee |
+ log.error("Exception during introspection", iex);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ return new BeanIntrospectionData(ictx.getPropertyDescriptors());
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
}
|
|
|
fc09ee |
diff --git a/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..9d4e3b3
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/main/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospector.java
|
|
|
fc09ee |
@@ -0,0 +1,75 @@
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.IntrospectionException;
|
|
|
fc09ee |
+import java.util.Collection;
|
|
|
fc09ee |
+import java.util.Collections;
|
|
|
fc09ee |
+import java.util.HashSet;
|
|
|
fc09ee |
+import java.util.Set;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * A specialized {@code BeanIntrospector} implementation which suppresses some properties.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * An instance of this class is passed a set with the names of the properties it should
|
|
|
fc09ee |
+ * process. During introspection of a bean class it removes all these properties from the
|
|
|
fc09ee |
+ * {@link IntrospectionContext}. So effectively, properties added by a different
|
|
|
fc09ee |
+ * {@code BeanIntrospector} are removed again.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ * @since 1.9.2
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public class SuppressPropertiesBeanIntrospector implements BeanIntrospector {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * A specialized instance which is configured to suppress the special {@code class}
|
|
|
fc09ee |
+ * properties of Java beans. Unintended access to the property {@code class} (which is
|
|
|
fc09ee |
+ * common to all Java objects) can be a security risk because it also allows access to
|
|
|
fc09ee |
+ * the class loader. Adding this instance as {@code BeanIntrospector} to an instance
|
|
|
fc09ee |
+ * of {@code PropertyUtilsBean} suppresses the {@code class} property; it can then no
|
|
|
fc09ee |
+ * longer be accessed.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public static final SuppressPropertiesBeanIntrospector SUPPRESS_CLASS =
|
|
|
fc09ee |
+ new SuppressPropertiesBeanIntrospector(Collections.singleton("class"));
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /** A set with the names of the properties to be suppressed. */
|
|
|
fc09ee |
+ private final Set<String> propertyNames;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Creates a new instance of {@code SuppressPropertiesBeanIntrospector} and sets the
|
|
|
fc09ee |
+ * names of the properties to be suppressed.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @param propertiesToSuppress the names of the properties to be suppressed (must not
|
|
|
fc09ee |
+ * be null)
|
|
|
fc09ee |
+ * @throws IllegalArgumentException if the collection with property names is
|
|
|
fc09ee |
+ * null
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public SuppressPropertiesBeanIntrospector(Collection<String> propertiesToSuppress) {
|
|
|
fc09ee |
+ if (propertiesToSuppress == null) {
|
|
|
fc09ee |
+ throw new IllegalArgumentException("Property names must not be null!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ propertyNames = Collections.unmodifiableSet(new HashSet<String>(
|
|
|
fc09ee |
+ propertiesToSuppress));
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns a (unmodifiable) set with the names of the properties which are suppressed
|
|
|
fc09ee |
+ * by this {@code BeanIntrospector}.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return a set with the names of the suppressed properties
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public Set<String> getSuppressedProperties() {
|
|
|
fc09ee |
+ return propertyNames;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * {@inheritDoc} This implementation removes all properties from the given context it
|
|
|
fc09ee |
+ * is configured for.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void introspect(IntrospectionContext icontext) throws IntrospectionException {
|
|
|
fc09ee |
+ for (String property : getSuppressedProperties()) {
|
|
|
fc09ee |
+ icontext.removePropertyDescriptor(property);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java b/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..ed93e70
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/test/java/org/apache/commons/beanutils/SuppressPropertiesBeanIntrospectorTestCase.java
|
|
|
fc09ee |
@@ -0,0 +1,127 @@
|
|
|
fc09ee |
+package org.apache.commons.beanutils;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import java.beans.IntrospectionException;
|
|
|
fc09ee |
+import java.beans.PropertyDescriptor;
|
|
|
fc09ee |
+import java.util.Arrays;
|
|
|
fc09ee |
+import java.util.Collection;
|
|
|
fc09ee |
+import java.util.HashSet;
|
|
|
fc09ee |
+import java.util.Set;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import junit.framework.TestCase;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ * Test class for {@code SuppressPropertiesBeanIntrospector}.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public class SuppressPropertiesBeanIntrospectorTestCase extends TestCase {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tries to create an instance without properties.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testInitNoPropertyNames() {
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ new SuppressPropertiesBeanIntrospector(null);
|
|
|
fc09ee |
+ fail("Missing properties not detected!");
|
|
|
fc09ee |
+ } catch (IllegalArgumentException iaex) {
|
|
|
fc09ee |
+ // ok
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tests whether the expected properties have been removed during introspection.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testRemovePropertiesDuringIntrospection() throws IntrospectionException {
|
|
|
fc09ee |
+ String[] properties = { "test", "other", "oneMore" };
|
|
|
fc09ee |
+ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector(
|
|
|
fc09ee |
+ Arrays.asList(properties));
|
|
|
fc09ee |
+ IntrospectionContextTestImpl context = new IntrospectionContextTestImpl();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ introspector.introspect(context);
|
|
|
fc09ee |
+ assertEquals("Wrong number of removed properties", properties.length, context
|
|
|
fc09ee |
+ .getRemovedProperties().size());
|
|
|
fc09ee |
+ for (String property : properties) {
|
|
|
fc09ee |
+ assertTrue("Property not removed: " + property, context
|
|
|
fc09ee |
+ .getRemovedProperties().contains(property));
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tests that a defensive copy is created from the collection with properties to be
|
|
|
fc09ee |
+ * removed.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testPropertyNamesDefensiveCopy() throws IntrospectionException {
|
|
|
fc09ee |
+ Collection<String> properties = new HashSet<String>();
|
|
|
fc09ee |
+ properties.add("prop1");
|
|
|
fc09ee |
+ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector(
|
|
|
fc09ee |
+ properties);
|
|
|
fc09ee |
+ properties.add("prop2");
|
|
|
fc09ee |
+ IntrospectionContextTestImpl context = new IntrospectionContextTestImpl();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ introspector.introspect(context);
|
|
|
fc09ee |
+ assertEquals("Wrong number of removed properties", 1, context
|
|
|
fc09ee |
+ .getRemovedProperties().size());
|
|
|
fc09ee |
+ assertTrue("Wrong removed property",
|
|
|
fc09ee |
+ context.getRemovedProperties().contains("prop1"));
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tests that the set with properties to be removed cannot be modified.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testGetSuppressedPropertiesModify() {
|
|
|
fc09ee |
+ SuppressPropertiesBeanIntrospector introspector = new SuppressPropertiesBeanIntrospector(
|
|
|
fc09ee |
+ Arrays.asList("p1", "p2"));
|
|
|
fc09ee |
+ Set<String> properties = introspector.getSuppressedProperties();
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ properties.add("anotherProperty");
|
|
|
fc09ee |
+ fail("Could modify properties");
|
|
|
fc09ee |
+ } catch (UnsupportedOperationException uoex) {
|
|
|
fc09ee |
+ // ok
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * A test implementation of IntrospectionContext which collects the properties which
|
|
|
fc09ee |
+ * have been removed.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ private static class IntrospectionContextTestImpl implements IntrospectionContext {
|
|
|
fc09ee |
+ /** Stores the names of properties which have been removed. */
|
|
|
fc09ee |
+ private final Set<String> removedProperties = new HashSet<String>();
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Returns the names of properties which have been removed.
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @return the set with removed properties
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public Set<String> getRemovedProperties() {
|
|
|
fc09ee |
+ return removedProperties;
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public Class getTargetClass() {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void addPropertyDescriptor(PropertyDescriptor desc) {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public boolean hasProperty(String name) {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public PropertyDescriptor getPropertyDescriptor(String name) {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public void removePropertyDescriptor(String name) {
|
|
|
fc09ee |
+ removedProperties.add(name);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ public Set<String> propertyNames() {
|
|
|
fc09ee |
+ throw new UnsupportedOperationException("Unexpected method call!");
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java b/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..31ac31c
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/test/java/org/apache/commons/beanutils/bugs/Jira463TestCase.java
|
|
|
fc09ee |
@@ -0,0 +1,48 @@
|
|
|
fc09ee |
+/*
|
|
|
fc09ee |
+ * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
fc09ee |
+ * contributor license agreements. See the NOTICE file distributed with
|
|
|
fc09ee |
+ * this work for additional information regarding copyright ownership.
|
|
|
fc09ee |
+ * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
fc09ee |
+ * (the "License"); you may not use this file except in compliance with
|
|
|
fc09ee |
+ * the License. You may obtain a copy of the License at
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
fc09ee |
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
fc09ee |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
fc09ee |
+ * See the License for the specific language governing permissions and
|
|
|
fc09ee |
+ * limitations under the License.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+package org.apache.commons.beanutils.bugs;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import junit.framework.TestCase;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import org.apache.commons.beanutils.AlphaBean;
|
|
|
fc09ee |
+import org.apache.commons.beanutils.BeanUtilsBean;
|
|
|
fc09ee |
+import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ * Class loader vulnerability in DefaultResolver
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @version $Id$
|
|
|
fc09ee |
+ * @see https://issues.apache.org/jira/browse/BEANUTILS-463
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public class Jira463TestCase extends TestCase {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Tests that with a specialized {@code BeanIntrospector} implementation the class
|
|
|
fc09ee |
+ * property can be suppressed.
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testSuppressClassProperty() throws Exception {
|
|
|
fc09ee |
+ BeanUtilsBean bub = new BeanUtilsBean();
|
|
|
fc09ee |
+ bub.getPropertyUtils().addBeanIntrospector(
|
|
|
fc09ee |
+ SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
|
|
|
fc09ee |
+ AlphaBean bean = new AlphaBean();
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ bub.getProperty(bean, "class");
|
|
|
fc09ee |
+ fail("Could access class property!");
|
|
|
fc09ee |
+ } catch (NoSuchMethodException ex) {
|
|
|
fc09ee |
+ // ok
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
diff --git a/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java b/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java
|
|
|
fc09ee |
new file mode 100644
|
|
|
fc09ee |
index 0000000..2a70811
|
|
|
fc09ee |
--- /dev/null
|
|
|
fc09ee |
+++ b/src/test/java/org/apache/commons/beanutils/bugs/Jira520TestCase.java
|
|
|
fc09ee |
@@ -0,0 +1,39 @@
|
|
|
fc09ee |
+package org.apache.commons.beanutils.bugs;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import org.apache.commons.beanutils.AlphaBean;
|
|
|
fc09ee |
+import org.apache.commons.beanutils.BeanUtilsBean;
|
|
|
fc09ee |
+import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+import junit.framework.TestCase;
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+/**
|
|
|
fc09ee |
+ * Fix CVE: https://nvd.nist.gov/vuln/detail/CVE-2014-0114
|
|
|
fc09ee |
+ *
|
|
|
fc09ee |
+ * @see https://issues.apache.org/jira/browse/BEANUTILS-520
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+public class Jira520TestCase extends TestCase {
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * By default opt-in to security that does not allow access to "class".
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testSuppressClassPropertyByDefault() throws Exception {
|
|
|
fc09ee |
+ final BeanUtilsBean bub = new BeanUtilsBean();
|
|
|
fc09ee |
+ final AlphaBean bean = new AlphaBean();
|
|
|
fc09ee |
+ try {
|
|
|
fc09ee |
+ bub.getProperty(bean, "class");
|
|
|
fc09ee |
+ fail("Could access class property!");
|
|
|
fc09ee |
+ } catch (final NoSuchMethodException ex) {
|
|
|
fc09ee |
+ // ok
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+
|
|
|
fc09ee |
+ /**
|
|
|
fc09ee |
+ * Allow opt-out to make your app less secure but allow access to "class".
|
|
|
fc09ee |
+ */
|
|
|
fc09ee |
+ public void testAllowAccessToClassProperty() throws Exception {
|
|
|
fc09ee |
+ final BeanUtilsBean bub = new BeanUtilsBean();
|
|
|
fc09ee |
+ bub.getPropertyUtils().removeBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
|
|
|
fc09ee |
+ final AlphaBean bean = new AlphaBean();
|
|
|
fc09ee |
+ String result = bub.getProperty(bean, "class");
|
|
|
fc09ee |
+ assertEquals("Class property should have been accessed", "class org.apache.commons.beanutils.AlphaBean", result);
|
|
|
fc09ee |
+ }
|
|
|
fc09ee |
+}
|
|
|
fc09ee |
\ No newline at end of file
|
|
|
fc09ee |
--
|
|
|
fc09ee |
2.21.0
|
|
|
fc09ee |
|