You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by di...@apache.org on 2002/03/24 10:16:11 UTC
cvs commit: jakarta-commons/beanutils/src/java/org/apache/commons/beanutils MappedPropertyDescriptor.java
dion 02/03/24 01:16:11
Modified: beanutils/src/java/org/apache/commons/beanutils
MappedPropertyDescriptor.java
Log:
Fixed bug 7309
Revision Changes Path
1.8 +517 -262 jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/MappedPropertyDescriptor.java
Index: MappedPropertyDescriptor.java
===================================================================
RCS file: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/MappedPropertyDescriptor.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- MappedPropertyDescriptor.java 23 Jan 2002 22:35:58 -0000 1.7
+++ MappedPropertyDescriptor.java 24 Mar 2002 09:16:11 -0000 1.8
@@ -1,62 +1,121 @@
-/*
- * $Header: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/MappedPropertyDescriptor.java,v 1.7 2002/01/23 22:35:58 sanders Exp $
- * $Revision: 1.7 $
- * $Date: 2002/01/23 22:35:58 $
- *
- * ====================================================================
- *
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution, if
- * any, must include the following acknowlegement:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowlegement may appear in the software itself,
- * if and wherever such third-party acknowlegements normally appear.
- *
- * 4. The names "The Jakarta Project", "Commons", and "Apache Software
- * Foundation" must not be used to endorse or promote products derived
- * from this software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache"
- * nor may "Apache" appear in their names without prior written
- * permission of the Apache Group.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
+/*
+
+ * $Header: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/MappedPropertyDescriptor.java,v 1.8 2002/03/24 09:16:11 dion Exp $
+
+ * $Revision: 1.8 $
+
+ * $Date: 2002/03/24 09:16:11 $
+
+ *
+
+ * ====================================================================
+
+ *
+
+ * The Apache Software License, Version 1.1
+
+ *
+
+ * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
+
+ * reserved.
+
+ *
+
+ * Redistribution and use in source and binary forms, with or without
+
+ * modification, are permitted provided that the following conditions
+
+ * are met:
+
+ *
+
+ * 1. Redistributions of source code must retain the above copyright
+
+ * notice, this list of conditions and the following disclaimer.
+
+ *
+
+ * 2. Redistributions in binary form must reproduce the above copyright
+
+ * notice, this list of conditions and the following disclaimer in
+
+ * the documentation and/or other materials provided with the
+
+ * distribution.
+
+ *
+
+ * 3. The end-user documentation included with the redistribution, if
+
+ * any, must include the following acknowlegement:
+
+ * "This product includes software developed by the
+
+ * Apache Software Foundation (http://www.apache.org/)."
+
+ * Alternately, this acknowlegement may appear in the software itself,
+
+ * if and wherever such third-party acknowlegements normally appear.
+
+ *
+
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+
+ * Foundation" must not be used to endorse or promote products derived
+
+ * from this software without prior written permission. For written
+
+ * permission, please contact apache@apache.org.
+
+ *
+
+ * 5. Products derived from this software may not be called "Apache"
+
+ * nor may "Apache" appear in their names without prior written
+
+ * permission of the Apache Group.
+
+ *
+
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+
+ * SUCH DAMAGE.
+
+ * ====================================================================
+
+ *
+
+ * This software consists of voluntary contributions made by many
+
+ * individuals on behalf of the Apache Software Foundation. For more
+
+ * information on the Apache Software Foundation, please see
+
+ * <http://www.apache.org/>.
+
+ *
+
*/
@@ -71,78 +130,127 @@
import java.security.PrivilegedAction;
-/**
- * A MappedPropertyDescriptor describes one mapped property.
- * Mapped properties are multivalued properties like indexed properties
- * but that are accessed with a String key instead of an index.
- * Such property values are typically stored in a Map collection.
- * For this class to work properly, a mapped value must have
- * getter and setter methods of the form
- * <p><code>get<strong>Property</strong>(String key)<code> and
- * <p><code>set<Property>(String key, Object value)<code>,
- * <p>where <code><strong>Property</strong></code> must be replaced
- * by the name of the property.
- * @see java.beans.PropertyDescriptor
- *
- * @author Rey Fran�ois
- * @author Gregor Ra�man
- * @version $Revision: 1.7 $ $Date: 2002/01/23 22:35:58 $
+/**
+
+ * A MappedPropertyDescriptor describes one mapped property.
+
+ * Mapped properties are multivalued properties like indexed properties
+
+ * but that are accessed with a String key instead of an index.
+
+ * Such property values are typically stored in a Map collection.
+
+ * For this class to work properly, a mapped value must have
+
+ * getter and setter methods of the form
+
+ * <p><code>get<strong>Property</strong>(String key)<code> and
+
+ * <p><code>set<Property>(String key, Object value)<code>,
+
+ * <p>where <code><strong>Property</strong></code> must be replaced
+
+ * by the name of the property.
+
+ * @see java.beans.PropertyDescriptor
+
+ *
+
+ * @author Rey Fran�ois
+
+ * @author Gregor Ra�man
+
+ * @version $Revision: 1.8 $ $Date: 2002/03/24 09:16:11 $
+
*/
public class MappedPropertyDescriptor extends PropertyDescriptor {
- // ----------------------------------------------------- Instance Variables
-
-
-
- /**
- * The underlying data type of the property we are describing.
+ // ----------------------------------------------------- Instance Variables
+
+
+
+
+
+
+
+ /**
+
+ * The underlying data type of the property we are describing.
+
*/
private Class mappedPropertyType;
- /**
- * The reader method for this property (if any).
+ /**
+
+ * The reader method for this property (if any).
+
*/
private Method mappedReadMethod;
- /**
- * The writer method for this property (if any).
+ /**
+
+ * The writer method for this property (if any).
+
*/
private Method mappedWriteMethod;
- /**
- * The parameter types array for the reader method signature.
+ /**
+
+ * The parameter types array for the reader method signature.
+
*/
- private static final Class[] stringClassArray = new Class[]{String.class};
-
-
- // ----------------------------------------------------------- Constructors
-
-
- /**
- * Constructs a MappedPropertyDescriptor for a property that follows
- * the standard Java convention by having getFoo and setFoo
- * accessor methods, with the addition of a String parameter (the key).
- * Thus if the argument name is "fred", it will
- * assume that the writer method is "setFred" and the reader method
- * is "getFred". Note that the property name should start with a lower
- * case character, which will be capitalized in the method names.
- *
- * @param propertyName The programmatic name of the property.
- * @param beanClass The Class object for the target bean. For
- * example sun.beans.OurButton.class.
- *
- * @exception IntrospectionException if an exception occurs during
- * introspection.
+ private static final Class[] stringClassArray = new Class[]{String.class};
+
+
+
+
+
+ // ----------------------------------------------------------- Constructors
+
+
+
+
+
+ /**
+
+ * Constructs a MappedPropertyDescriptor for a property that follows
+
+ * the standard Java convention by having getFoo and setFoo
+
+ * accessor methods, with the addition of a String parameter (the key).
+
+ * Thus if the argument name is "fred", it will
+
+ * assume that the writer method is "setFred" and the reader method
+
+ * is "getFred". Note that the property name should start with a lower
+
+ * case character, which will be capitalized in the method names.
+
+ *
+
+ * @param propertyName The programmatic name of the property.
+
+ * @param beanClass The Class object for the target bean. For
+
+ * example sun.beans.OurButton.class.
+
+ *
+
+ * @exception IntrospectionException if an exception occurs during
+
+ * introspection.
+
*/
public MappedPropertyDescriptor(String propertyName, Class beanClass)
@@ -161,9 +269,12 @@
setName(propertyName);
- String base = capitalize(propertyName);
-
- // Look for mapped get and set methods
+ String base = capitalize(propertyName);
+
+
+
+ // Look for mapped get and set methods
+
try {
mappedReadMethod = findMethod(beanClass, "get" + base, 1,
@@ -198,22 +309,38 @@
}
- /**
- * This constructor takes the name of a mapped property, and method
- * names for reading and writing the property.
- *
- * @param propertyName The programmatic name of the property.
- * @param beanClass The Class object for the target bean. For
- * example sun.beans.OurButton.class.
- * @param mappedGetterName The name of the method used for
- * reading one of the property values. May be null if the
- * property is write-only.
- * @param mappedSetterName The name of the method used for writing
- * one of the property values. May be null if the property is
- * read-only.
- *
- * @exception IntrospectionException if an exception occurs during
- * introspection.
+ /**
+
+ * This constructor takes the name of a mapped property, and method
+
+ * names for reading and writing the property.
+
+ *
+
+ * @param propertyName The programmatic name of the property.
+
+ * @param beanClass The Class object for the target bean. For
+
+ * example sun.beans.OurButton.class.
+
+ * @param mappedGetterName The name of the method used for
+
+ * reading one of the property values. May be null if the
+
+ * property is write-only.
+
+ * @param mappedSetterName The name of the method used for writing
+
+ * one of the property values. May be null if the property is
+
+ * read-only.
+
+ *
+
+ * @exception IntrospectionException if an exception occurs during
+
+ * introspection.
+
*/
public MappedPropertyDescriptor(String propertyName, Class beanClass,
@@ -232,16 +359,20 @@
}
- setName(propertyName);
-
- // search the mapped get and set methods
+ setName(propertyName);
+
+
+
+ // search the mapped get and set methods
+
mappedReadMethod =
findMethod(beanClass, mappedGetterName, 1, stringClassArray);
if (mappedReadMethod != null) {
- Class params[] = { String.class,
+ Class params[] = { String.class,
+
mappedReadMethod.getReturnType() };
mappedWriteMethod =
@@ -262,19 +393,32 @@
}
- /**
- * This constructor takes the name of a mapped property, and Method
- * objects for reading and writing the property.
- *
- * @param propertyName The programmatic name of the property.
- * @param mappedGetter The method used for reading one of
- * the property values. May be be null if the property
- * is write-only.
- * @param mappedSetter The method used for writing one the
- * property values. May be null if the property is read-only.
- *
- * @exception IntrospectionException if an exception occurs during
- * introspection.
+ /**
+
+ * This constructor takes the name of a mapped property, and Method
+
+ * objects for reading and writing the property.
+
+ *
+
+ * @param propertyName The programmatic name of the property.
+
+ * @param mappedGetter The method used for reading one of
+
+ * the property values. May be be null if the property
+
+ * is write-only.
+
+ * @param mappedSetter The method used for writing one the
+
+ * property values. May be null if the property is read-only.
+
+ *
+
+ * @exception IntrospectionException if an exception occurs during
+
+ * introspection.
+
*/
public MappedPropertyDescriptor(String propertyName,
@@ -302,21 +446,36 @@
findMappedPropertyType();
- }
-
-
- // -------------------------------------------------------- Public Methods
-
-
- /**
- * Gets the Class object for the property values.
- *
- * @return The Java type info for the property values. Note that
- * the "Class" object may describe a built-in Java type such as "int".
- * The result may be "null" if this is a mapped property that
- * does not support non-keyed access.
- * <p>
- * This is the type that will be returned by the mappedReadMethod.
+ }
+
+
+
+
+
+ // -------------------------------------------------------- Public Methods
+
+
+
+
+
+ /**
+
+ * Gets the Class object for the property values.
+
+ *
+
+ * @return The Java type info for the property values. Note that
+
+ * the "Class" object may describe a built-in Java type such as "int".
+
+ * The result may be "null" if this is a mapped property that
+
+ * does not support non-keyed access.
+
+ * <p>
+
+ * This is the type that will be returned by the mappedReadMethod.
+
*/
public Class getMappedPropertyType() {
@@ -328,11 +487,16 @@
}
- /**
- * Gets the method that should be used to read one of the property value.
- *
- * @return The method that should be used to read the property value.
- * May return null if the property can't be read.
+ /**
+
+ * Gets the method that should be used to read one of the property value.
+
+ *
+
+ * @return The method that should be used to read the property value.
+
+ * May return null if the property can't be read.
+
*/
public Method getMappedReadMethod() {
@@ -344,10 +508,14 @@
}
- /**
- * Sets the method that should be used to read one of the property value.
- *
- * @param getter The new getter method.
+ /**
+
+ * Sets the method that should be used to read one of the property value.
+
+ *
+
+ * @param getter The new getter method.
+
*/
public void setMappedReadMethod(Method mappedGetter)
@@ -363,11 +531,16 @@
}
- /**
- * Gets the method that should be used to write one of the property value.
- *
- * @return The method that should be used to write one of the property value.
- * May return null if the property can't be written.
+ /**
+
+ * Gets the method that should be used to write one of the property value.
+
+ *
+
+ * @return The method that should be used to write one of the property value.
+
+ * May return null if the property can't be written.
+
*/
public Method getMappedWriteMethod() {
@@ -379,10 +552,14 @@
}
- /**
- * Sets the method that should be used to write the property value.
- *
- * @param setter The new setter method.
+ /**
+
+ * Sets the method that should be used to write the property value.
+
+ *
+
+ * @param setter The new setter method.
+
*/
public void setMappedWriteMethod(Method mappedSetter)
@@ -395,15 +572,24 @@
findMappedPropertyType();
- }
-
-
- // ------------------------------------------------------- Private Methods
-
-
- /**
- * Introspect our bean class to identify the corresponding getter
- * and setter methods.
+ }
+
+
+
+
+
+ // ------------------------------------------------------- Private Methods
+
+
+
+
+
+ /**
+
+ * Introspect our bean class to identify the corresponding getter
+
+ * and setter methods.
+
*/
private void findMappedPropertyType() throws IntrospectionException {
@@ -475,10 +661,14 @@
}
- /**
- * Return a capitalized version of the specified property name.
- *
- * @param s The property name
+ /**
+
+ * Return a capitalized version of the specified property name.
+
+ *
+
+ * @param s The property name
+
*/
private static String capitalize(String s) {
@@ -497,27 +687,43 @@
return new String(chars);
- }
-
-
- //======================================================================
- // Package private support methods (copied from java.beans.Introspector).
- //======================================================================
-
- // Cache of Class.getDeclaredMethods:
+ }
+
+
+
+
+
+ //======================================================================
+
+ // Package private support methods (copied from java.beans.Introspector).
+
+ //======================================================================
+
+
+
+ // Cache of Class.getDeclaredMethods:
+
private static java.util.Hashtable
- declaredMethodCache = new java.util.Hashtable();
-
-
- /*
- * Internal method to return *public* methods within a class.
- */
+ declaredMethodCache = new java.util.Hashtable();
+
+
+
+
+
+ /*
+
+ * Internal method to return *public* methods within a class.
+
+ */
+
private static synchronized Method[] getPublicDeclaredMethods(Class clz) {
- // Looking up Class.getDeclaredMethods is relatively expensive,
- // so we cache the results.
+ // Looking up Class.getDeclaredMethods is relatively expensive,
+
+ // so we cache the results.
+
final Class fclz = clz;
Method[] result = (Method[]) declaredMethodCache.get(fclz);
@@ -526,9 +732,12 @@
return result;
- }
-
- // We have to raise privilege for getDeclaredMethods
+ }
+
+
+
+ // We have to raise privilege for getDeclaredMethods
+
result = (Method[])
AccessController.doPrivileged(new PrivilegedAction() {
@@ -539,9 +748,12 @@
}
- });
-
- // Null out any non-public methods.
+ });
+
+
+
+ // Null out any non-public methods.
+
for (int i = 0; i < result.length; i++) {
Method method = result[i];
@@ -554,9 +766,12 @@
}
- }
-
- // Add it to the cache.
+ }
+
+
+
+ // Add it to the cache.
+
declaredMethodCache.put(clz, result);
return result;
@@ -565,8 +780,10 @@
}
- /**
- * Internal support for finding a target methodName on a given class.
+ /**
+
+ * Internal support for finding a target methodName on a given class.
+
*/
private static Method internalFindMethod(Class start, String methodName,
@@ -574,8 +791,10 @@
int argCount) {
- // For overridden methods we need to find the most derived version.
- // So we start with the given class and walk up the superclass chain.
+ // For overridden methods we need to find the most derived version.
+
+ // So we start with the given class and walk up the superclass chain.
+
for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
Method methods[] = getPublicDeclaredMethods(cl);
@@ -588,8 +807,10 @@
continue;
- }
- // skip static methods.
+ }
+
+ // skip static methods.
+
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
@@ -608,11 +829,16 @@
}
- }
-
- // Now check any inherited interfaces. This is necessary both when
- // the argument class is itself an interface, and when the argument
- // class is an abstract class.
+ }
+
+
+
+ // Now check any inherited interfaces. This is necessary both when
+
+ // the argument class is itself an interface, and when the argument
+
+ // class is an abstract class.
+
Class ifcs[] = start.getInterfaces();
for (int i = 0; i < ifcs.length; i++) {
@@ -634,9 +860,12 @@
}
- /**
- * Internal support for finding a target methodName with a given
- * parameter list on a given class.
+ /**
+
+ * Internal support for finding a target methodName with a given
+
+ * parameter list on a given class.
+
*/
private static Method internalFindMethod(Class start, String methodName,
@@ -644,8 +873,10 @@
int argCount, Class args[]) {
- // For overriden methods we need to find the most derived version.
- // So we start with the given class and walk up the superclass chain.
+ // For overriden methods we need to find the most derived version.
+
+ // So we start with the given class and walk up the superclass chain.
+
for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
Method methods[] = getPublicDeclaredMethods(cl);
@@ -658,16 +889,20 @@
continue;
- }
- // skip static methods.
+ }
+
+ // skip static methods.
+
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
continue;
- }
- // make sure method signature matches.
+ }
+
+ // make sure method signature matches.
+
Class params[] = method.getParameterTypes();
if (method.getName().equals(methodName) &&
@@ -704,11 +939,16 @@
}
- }
-
- // Now check any inherited interfaces. This is necessary both when
- // the argument class is itself an interface, and when the argument
- // class is an abstract class.
+ }
+
+
+
+ // Now check any inherited interfaces. This is necessary both when
+
+ // the argument class is itself an interface, and when the argument
+
+ // class is an abstract class.
+
Class ifcs[] = start.getInterfaces();
for (int i = 0; i < ifcs.length; i++) {
@@ -730,8 +970,10 @@
}
- /**
- * Find a target methodName on a given class.
+ /**
+
+ * Find a target methodName on a given class.
+
*/
static Method findMethod(Class cls, String methodName, int argCount)
@@ -752,9 +994,12 @@
return m;
- }
-
- // We failed to find a suitable method
+ }
+
+
+
+ // We failed to find a suitable method
+
throw new IntrospectionException("No method \"" + methodName +
"\" with " + argCount + " arg(s)");
@@ -762,8 +1007,10 @@
}
- /**
- * Find a target methodName with specific parameter list on a given class.
+ /**
+
+ * Find a target methodName with specific parameter list on a given class.
+
*/
static Method findMethod(Class cls, String methodName, int argCount,
@@ -784,9 +1031,12 @@
return m;
- }
-
- // We failed to find a suitable method
+ }
+
+
+
+ // We failed to find a suitable method
+
throw new IntrospectionException("No method \"" + methodName +
"\" with " + argCount + " arg(s) of matching types.");
@@ -794,19 +1044,21 @@
}
- /**
- * Return true if class a is either equivalent to class b, or
- * if class a is a subclass of class b, i.e. if a either "extends"
- * or "implements" b.
- * Note tht either or both "Class" objects may represent interfaces.
+ /**
+ * Return true if class a is either equivalent to class b, or
+ * if class a is a subclass of class b, ie if a either "extends"
+ * or "implements" b.
+ * Note tht either or both "Class" objects may represent interfaces.
*/
-
static boolean isSubclass(Class a, Class b) {
- // We rely on the fact that for any given java class or
- // primtitive type there is a unqiue Class object, so
- // we can use object equivalence in the comparisons.
+ // We rely on the fact that for any given java class or
+
+ // primtitive type there is a unqiue Class object, so
+
+ // we can use object equivalence in the comparisons.
+
if (a == b) {
return true;
@@ -851,8 +1103,10 @@
}
- /**
- * Return true iff the given method throws the given exception.
+ /**
+
+ * Return true iff the given method throws the given exception.
+
*/
private boolean throwsException(Method method, Class exception) {
@@ -876,4 +1130,5 @@
}
-}
+}
+
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>