commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mattias Jiderhamn <mj-li...@expertsystem.se>
Subject Re: [sandbox/i18n] Could sombody commit my JDBC provider?
Date Thu, 14 Jul 2005 05:41:09 GMT
As you may have noticed, I chose to create a 
Bugzilla entry (35726, 
http://issues.apache.org/bugzilla/show_bug.cgi?id=35726), 
where I intend to post the patches for the 
remaining issues, so that they are readily available to anyone.

At 2005-07-13 22:35, Mattias Jiderhamn wrote:
>I haven't heard from Daniel Florey for a while 
>now, so my guess is he is on vacation. Therefore 
>I wonder if there is somebody else with sandbox 
>commit access that could commit my patch below, 
>adding a JDBC/database MessageProvider to the i18n component.
>
>I intend to continue working on the remaining 
>issues (as of e-mail on 2005-06-18) and I would 
>prefer to separate the different issues into 
>different patches/commits, rather than one big "my contributions".
>
>If needed for correct encoding/line breaks, I 
>could e-mail a zipped diff to somebody privately, or create a Bugzilla entry.
>
>Thanks in advance.
>   Mattias Jiderhamn
>
>Here is the patch:
>--------------------------------------------------------
>
>Index: project.properties
>===================================================================
>--- project.properties  (revision 201952)
>+++ project.properties  (working copy)
>@@ -28,3 +28,8 @@
>  # M A V E N  J A R  O V E R R I D E
>  # ------------------------------------------------------------------------
>  maven.jar.override = on
>+
>+
>+# Set target to Java 1.4, since JCoverage does not work with Java 1.5
>+maven.compile.source=1.4
>+maven.compile.target=1.4
>\ No newline at end of file
>Index: project.xml
>===================================================================
>--- project.xml (revision 201952)
>+++ project.xml (working copy)
>@@ -74,6 +74,23 @@
>      </contributors>
>
>      <dependencies>
>+        <!-- Please note, these depenencies are 
>needed for unit tests only! -->
>+        <dependency>
>+            <id>hsqldb</id>
>+            <version>1.7.3.3</version>
>+        </dependency>
>+        <dependency>
>+            <id>commons-dbcp</id>
>+            <version>1.2.1</version>
>+        </dependency>
>+        <dependency>
>+            <id>commons-pool</id>
>+            <version>1.2</version>
>+        </dependency>
>+        <dependency>
>+            <id>commons-collections</id>
>+            <version>2.1.1</version>
>+        </dependency>
>      </dependencies>
>
>      <build>
>Index: src/test/org/apache/commons/i18n/I18nUtilsTest.java
>===================================================================
>--- src/test/org/apache/commons/i18n/I18nUtilsTest.java (revision 0)
>+++ src/test/org/apache/commons/i18n/I18nUtilsTest.java (revision 0)
>@@ -0,0 +1,45 @@
>+/*
>+*
>+* ====================================================================
>+*
>+* Copyright 2004 The Apache Software Foundation
>+*
>+* Licensed 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.commons.i18n;
>+
>+import junit.framework.TestCase;
>+
>+import java.util.Locale;
>+
>+/**
>+ * @author Mattias Jiderhamn
>+ */
>+public class I18nUtilsTest extends TestCase {
>+    public void testGetParentLocale() {
>+        assertEquals("Language, country and variant",
>+                new Locale("en", "GB"),
>+                I18nUtils.getParentLocale(new 
>Locale("en", "GB", "scottish")));
>+
>+        assertEquals("Language and country",
>+                Locale.ENGLISH,
>+                I18nUtils.getParentLocale(new Locale("en", "GB")));
>+
>+        assertEquals("Language and variant",
>+                Locale.ENGLISH,
>+                I18nUtils.getParentLocale(new Locale("en", "", "scottish")));
>+
>+        assertNull("Language only", 
>I18nUtils.getParentLocale(Locale.ENGLISH));
>+    }
>+}
>Index: src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java
>===================================================================
>--- 
>src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java 
>(revision 201952)
>+++ 
>src/test/org/apache/commons/i18n/ResourceBundleMessageProviderTest.java 
>(working copy)
>@@ -137,7 +137,7 @@
>          Map germanEntries = new 
> ResourceBundleMessageProvider("messageBundle").getEntries("helloWorld", 
> Locale.GERMAN);
>          assertEquals("No of entries", 3, germanEntries.size());
>          assertEquals("Hallo Welt", germanEntries.get("title"));
>-        assertEquals("Ich w�nsche Dir alles 
>Gute und ein frohes Fest!", germanEntries.get("text"));
>+        assertEquals("Ich wünsche Dir alles 
>Gute und ein frohes Fest!", germanEntries.get("text"));
>          assertEquals("This entry is not 
> translated to any other languages", germanEntries.get("notTranslated"));
>
>          Map frenchEntries = new 
> ResourceBundleMessageProvider("messageBundle").getEntries("helloWorld", 
> Locale.FRENCH);
>Index: src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java
>===================================================================
>--- 
>src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java 
>(revision 0)
>+++ 
>src/test/org/apache/commons/i18n/JdbcMessageProviderTest.java 
>(revision 0)
>@@ -0,0 +1,169 @@
>+package org.apache.commons.i18n;
>+
>+import junit.framework.TestCase;
>+
>+import java.sql.DriverManager;
>+import java.sql.Connection;
>+import java.sql.SQLException;
>+import java.sql.Statement;
>+import java.util.Locale;
>+import java.util.Map;
>+import java.util.Properties;
>+
>+import org.apache.commons.dbcp.BasicDataSource;
>+
>+/**
>+ * @author Mattias Jiderhamn
>+ */
>+public class JdbcMessageProviderTest extends TestCase {
>+
>+    private static Connection getNewConnection() throws SQLException {
>+        return 
>DriverManager.getConnection("jdbc:hsqldb:.", 
>"sa", ""); // Connect to in-memory database
>+    }
>+
>+    public void setUp() throws Exception {
>+        /* Make sure en_US is the default Locale for tests */
>+        Locale.setDefault(Locale.US);
>+
>+        Class.forName("org.hsqldb.jdbcDriver"); 
>// Load HSQLDB database driver
>+        Connection conn = getNewConnection();
>+        Statement stmt = conn.createStatement();
>+        stmt.execute(
>+                "CREATE TABLE messages ( " +
>+                "  'id' VARCHAR(30), " +
>+                "  'language' VARCHAR(2), " +
>+                "  'title' VARCHAR(100), " +
>+                "  'text' VARCHAR(100)" +
>+                ")");
>+        stmt.execute(
>+                "INSERT INTO messages VALUES (" +
>+                "  'helloWorld', 'en', " +
>+                "  'Hello World', 'I wish you a merry christmas!'" +
>+                ")"
>+        );
>+        stmt.execute(
>+                "INSERT INTO messages VALUES (" +
>+                "  'helloWorld', 'de', " +
>+                "  'Hallo Welt', 'Ich wünsche 
>Dir alles Gute und ein frohes Fest!'" +
>+                ")"
>+        );
>+        stmt.close();
>+        conn.close();
>+    }
>+
>+    public void tearDown() throws Exception {
>+        Connection conn = getNewConnection();
>+        conn.createStatement().execute(
>+                "DROP TABLE messages"
>+        );
>+        conn.close();
>+    }
>+
>+    public void testConstructors() throws Exception {
>+        // Connection constructor
>+        Connection conn = 
>DriverManager.getConnection("jdbc:hsqldb:.", 
>"sa", ""); // Connect to in-memory database
>+        JdbcMessageProvider jdbcMessageProvider 
>= new JdbcMessageProvider(conn, "messages", "id", "language");
>+        conn.close();
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
>+
>+        // DataSource constructor
>+        BasicDataSource dataSource = new BasicDataSource();
>+        dataSource.setUrl("jdbc:hsqldb:.");
>+        dataSource.setUsername("sa");
>+        dataSource.setPassword("");
>+        jdbcMessageProvider = new 
>JdbcMessageProvider(dataSource, "messages", "id", "language");
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
>+
>+        // Map/Properties constructor
>+        Properties props = new Properties();
>+        props.setProperty("jdbc.connect.driver", "org.hsqldb.jdbcDriver");
>+        props.setProperty("jdbc.connect.url", "jdbc:hsqldb:.");
>+        props.setProperty("jdbc.connect.login", "sa");
>+        props.setProperty("jdbc.connect.password", "");
>+
>+        props.setProperty("jdbc.sql.table", "messages");
>+        props.setProperty("jdbc.sql.key.column", "id");
>+        props.setProperty("jdbc.sql.locale.column", "language");
>+        jdbcMessageProvider = new JdbcMessageProvider(props);
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
>+
>+        // Test install
>+        MessageManager.addMessageProvider("messages", jdbcMessageProvider);
>+        assertEquals("Hello World", 
>MessageManager.getText("helloWorld", "title", null, Locale.ENGLISH));
>+    }
>+
>+    public void testGetText() throws Exception {
>+        Connection conn = 
>DriverManager.getConnection("jdbc:hsqldb:.", 
>"sa", ""); // Connect to in-memory database
>+        JdbcMessageProvider jdbcMessageProvider 
>= new JdbcMessageProvider(conn, "messages", "id", "language");
>+        conn.close();
>+
>+        // Explicit default locale
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.ENGLISH));
>+        assertEquals("I wish you a merry 
>christmas!", 
>jdbcMessageProvider.getText("helloWorld", "text", Locale.ENGLISH));
>+
>+        // Default locale with country
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.US));
>+        assertEquals("I wish you a merry 
>christmas!", jdbcMessageProvider.getText("helloWorld", "text", Locale.US));
>+
>+        // Default locale with country and variant
>+        Locale scottish = new Locale("en", "", "scottish");
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", scottish));
>+        assertEquals("I wish you a merry 
>christmas!", jdbcMessageProvider.getText("helloWorld", "text", scottish));
>+
>+        assertEquals("Hallo Welt", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.GERMAN));
>+        assertEquals("Ich wünsche Dir alles 
>Gute und ein frohes Fest!", 
>jdbcMessageProvider.getText("helloWorld", "text", Locale.GERMAN));
>+
>+        // Default locale with country
>+        assertEquals("Hallo Welt", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.GERMANY));
>+        assertEquals("Ich wünsche Dir alles 
>Gute und ein frohes Fest!", 
>jdbcMessageProvider.getText("helloWorld", "text", Locale.GERMANY));
>+
>+        // Test use of defaule
>+        assertEquals("Hello World", 
>jdbcMessageProvider.getText("helloWorld", "title", Locale.JAPANESE));
>+        assertEquals("I wish you a merry 
>christmas!", 
>jdbcMessageProvider.getText("helloWorld", "text", Locale.JAPANESE));
>+
>+        // Test non-existent
>+ 
>assertNull(jdbcMessageProvider.getText("foo", "bar", Locale.ENGLISH));
>+    }
>+
>+    public void testGetEntries() throws Exception {
>+        Connection conn = 
>DriverManager.getConnection("jdbc:hsqldb:.", 
>"sa", ""); // Connect to in-memory database
>+        JdbcMessageProvider jdbcMessageProvider 
>= new JdbcMessageProvider(conn, "messages", "id", "language");
>+        conn.close();
>+
>+        // Explicit default locale
>+        Map entries = 
>jdbcMessageProvider.getEntries("helloWorld", Locale.ENGLISH);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hello World", (String)entries.get("title"));
>+        assertEquals("I wish you a merry 
>christmas!", (String)entries.get("text"));
>+
>+        // Default locale with country
>+        entries = jdbcMessageProvider.getEntries("helloWorld", Locale.US);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hello World", (String)entries.get("title"));
>+        assertEquals("I wish you a merry 
>christmas!", (String)entries.get("text"));
>+
>+        // Default locale with country and variant
>+        Locale scottish = new Locale("en", "", "scottish");
>+        entries = jdbcMessageProvider.getEntries("helloWorld", scottish);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hello World", (String)entries.get("title"));
>+        assertEquals("I wish you a merry 
>christmas!", (String)entries.get("text"));
>+
>+        entries = 
>jdbcMessageProvider.getEntries("helloWorld", Locale.GERMAN);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hallo Welt", (String)entries.get("title"));
>+        assertEquals("Ich wünsche Dir alles 
>Gute und ein frohes Fest!", (String)entries.get("text"));
>+
>+        // Default locale with country
>+        entries = 
>jdbcMessageProvider.getEntries("helloWorld", Locale.GERMANY);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hallo Welt", (String)entries.get("title"));
>+        assertEquals("Ich wünsche Dir alles 
>Gute und ein frohes Fest!", (String)entries.get("text"));
>+
>+        // Test use of defaule
>+        entries = 
>jdbcMessageProvider.getEntries("helloWorld", Locale.JAPANESE);
>+        assertEquals("No of entries", 2, entries.size());
>+        assertEquals("Hello World", (String)entries.get("title"));
>+        assertEquals("I wish you a merry 
>christmas!", (String)entries.get("text"));
>+    }
>+}
>Index: src/java/org/apache/commons/i18n/XMLMessageProvider.java
>===================================================================
>--- 
>src/java/org/apache/commons/i18n/XMLMessageProvider.java    (revision 201952)
>+++ src/java/org/apache/commons/i18n/XMLMessageProvider.java    (working copy)
>@@ -123,9 +123,11 @@
>      private Message lookupMessage(String id, Locale locale) {
>          String key = id + '_' + locale.toString();
>          if (messages.containsKey(key)) return (Message)messages.get(key);
>-        while (key.lastIndexOf('_') > 0) {
>-            key = key.substring(0, key.lastIndexOf('_'));
>+        locale = I18nUtils.getParentLocale(locale);
>+        while (locale != null) {
>+            key = id + '_' + locale.toString();
>              if (messages.containsKey(key)) 
> return (Message)messages.get(key);
>+            locale = I18nUtils.getParentLocale(locale);
>          }
>          return null;
>      }
>Index: src/java/org/apache/commons/i18n/I18nUtils.java
>===================================================================
>--- src/java/org/apache/commons/i18n/I18nUtils.java     (revision 0)
>+++ src/java/org/apache/commons/i18n/I18nUtils.java     (revision 0)
>@@ -0,0 +1,40 @@
>+/*
>+*
>+* ====================================================================
>+*
>+* Copyright 2004 The Apache Software Foundation
>+*
>+* Licensed 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.commons.i18n;
>+
>+import java.util.Locale;
>+
>+/**
>+ * This class holds utility methods useful when working with i18n.
>+ * @author Mattias Jiderhamn
>+ */
>+public class I18nUtils {
>+    private I18nUtils() {
>+    }
>+
>+    public static Locale getParentLocale (Locale locale) {
>+        if(locale.getVariant().length() != 0)
>+          return new Locale(locale.getLanguage(), locale.getCountry());
>+        else if(locale.getCountry().length() != 0)
>+            return new Locale(locale.getLanguage());
>+        else // Locale with only language have no parent
>+            return null;
>+    }
>+}
>\ No newline at end of file
>Index: src/java/org/apache/commons/i18n/JdbcMessageProvider.java
>===================================================================
>--- src/java/org/apache/commons/i18n/JdbcMessageProvider.java   (revision 0)
>+++ src/java/org/apache/commons/i18n/JdbcMessageProvider.java   (revision 0)
>@@ -0,0 +1,183 @@
>+package org.apache.commons.i18n;
>+
>+import javax.sql.DataSource;
>+import java.util.*;
>+import java.sql.*;
>+
>+/**
>+ * @author Mattias Jiderhamn
>+ */
>+public class JdbcMessageProvider implements MessageProvider {
>+    /**
>+     * This Map has locale or language as key, and a Map with the different
>+     * messages as value.
>+     */
>+    private final Map locales = new HashMap();
>+
>+    private String idColumn;
>+
>+    private String languageColumn;
>+
>+    public JdbcMessageProvider(Connection conn, 
>String table, String idColumn, String languageColumn)
>+            throws SQLException {
>+        this.idColumn = idColumn;
>+        this.languageColumn = languageColumn;
>+        init(conn, table);
>+    }
>+
>+    public JdbcMessageProvider(DataSource ds, 
>String table, String idColumn, String languageColumn)
>+            throws SQLException {
>+        this.idColumn = idColumn;
>+        this.languageColumn = languageColumn;
>+        Connection conn = null;
>+        try {
>+            conn = ds.getConnection();
>+            init(conn, table);
>+        }
>+        finally {
>+            if(conn != null)
>+                conn.close();
>+        }
>+    }
>+
>+    /**
>+     * Create JDBC MessageProvider from properties in a Map, such
>+     * as a java.util.Properties object. The 
>following are the properties in use, which
>+     * are the same as for JDBCResources of Jakarta Commons Resources
>+     * jdbc.connect.driver               = org.gjt.mm.mysql.Driver
>+     * jdbc.connect.url                  = jdbc:mysql://localhost/resources
>+     * jdbc.connect.login                = resourcesTest
>+     * jdbc.connect.password             = resourcesTest
>+     *
>+     * jdbc.sql.table                    = resources
>+     * jdbc.sql.locale.column            = locale
>+     * jdbc.sql.key.column               = msgKey
>+     */
>+    public JdbcMessageProvider(Map properties) 
>throws ClassNotFoundException, SQLException {
>+        String driver = (String)properties.get("jdbc.connect.driver");
>+        String url    = (String)properties.get("jdbc.connect.url");
>+        String user = (String)properties.get("jdbc.connect.login");
>+        String pass = (String)properties.get("jdbc.connect.password");
>+
>+        String table = (String)properties.get("jdbc.sql.table");
>+        this.idColumn = (String)properties.get("jdbc.sql.key.column");
>+        this.languageColumn = 
>(String)properties.get("jdbc.sql.locale.column");
>+
>+        Class.forName(driver);
>+        Connection conn = null;
>+        try {
>+            conn = DriverManager.getConnection(url, user, pass);
>+            init(conn, table);
>+        }
>+        finally {
>+            if(conn != null)
>+                conn.close();
>+        }
>+    }
>+
>+    ///////////////////////////////////////////////////////////////////////
>+    // Methods for initialization
>+    ///////////////////////////////////////////////////////////////////////
>+
>+    private void init(Connection conn, String table) throws SQLException {
>+        Statement stmt = null;
>+        ResultSet rs = null;
>+        try {
>+            stmt = conn.createStatement();
>+            rs = stmt.executeQuery("SELECT * FROM " + table);
>+            String[] valueColumns = getValueColumns(rs);
>+            while(rs.next()) {
>+                String id = rs.getString(idColumn);
>+                Locale locale = getLocale(rs);
>+                Map entries = new HashMap();
>+                for(int i = 0; i < valueColumns.length; i++) {
>+ 
>entries.put(valueColumns[i], rs.getString(valueColumns[i]));
>+                }
>+                Map localeMap = (Map)locales.get(locale);
>+                if(localeMap == null) { // If first record for this Locale
>+                    localeMap = new HashMap();
>+                    locales.put(locale, localeMap);
>+                }
>+                localeMap.put(id, entries);
>+            }
>+        }
>+        finally {
>+            if(stmt != null)
>+                stmt.close();
>+            if(rs != null)
>+              rs.close();
>+        }
>+    }
>+
>+    /**
>+     * Get a String of all the column names, except the ID column and the
>+     * language column.
>+     * @param rs A <code>ResultSet</code> ready for reading meta data.
>+     * @return A String array with the text value column names.
>+     * @throws SQLException If an SQL error occurs.
>+     */
>+    protected String[] getValueColumns(ResultSet rs) throws SQLException {
>+        List output = new LinkedList();
>+        ResultSetMetaData metadata = rs.getMetaData();
>+        int count = metadata.getColumnCount();
>+        for(int i = 0; i < count; i++) {
>+            String columnName = 
>metadata.getColumnName(i+1); // (Count from 1)
>+            if(! idColumn.equals(columnName) && 
>! languageColumn.equals(columnName) )
>+                output.add(columnName);
>+        }
>+        return (String[])output.toArray(new String[0]);
>+    }
>+
>+    /**
>+     * Get <code>Locale</code> for the current 
>record in the ResultSet. May be overridden
>+     * by subclasses to allow for proprietary 
>interpretation of language data.
>+     * The default implementation assumes the 
>column with the name provided as languageColumn
>+     * for the constructor contains the ISO-639 code.
>+     * @return The <code>Locale</code> of the 
>current <code>ResultSet</code> record.
>+     */
>+    protected Locale getLocale(ResultSet rs) throws SQLException {
>+        return new Locale(rs.getString(languageColumn));
>+    }
>+
>+    ///////////////////////////////////////////////////////////////////////
>+    // Methods to implement MessageProvider
>+    ///////////////////////////////////////////////////////////////////////
>+
>+    public String getText(String id, String 
>entry, Locale locale) throws MessageNotFoundException {
>+        // TODO: Add Logging
>+        Map entries = getEntries(id, locale);
>+        if(entries != null) {
>+            // TODO: Consider whether we need 
>to recurse up if entries does not contain requested entry
>+            return (String)entries.get(entry);
>+        }
>+        else
>+            return null;
>+    }
>+
>+    public Map getEntries(String id, Locale 
>locale) throws MessageNotFoundException {
>+        Map entries = findEntriesRecursively(id,locale);
>+        if(entries == null) // If not found by 
>using specified locale, try to use default
>+            entries = findEntriesRecursively(id,Locale.getDefault());
>+        return entries;
>+    }
>+
>+    /**
>+     * Find entries by looking at the parent 
>locale (language, country, variant ->
>+     * language, country -> language) until 
>entry is found. If entry not found for topmost
>+     * Locale (language only), null is returned.
>+     */
>+    private Map findEntriesRecursively(String id, Locale locale) {
>+        Map localeIds = (Map)locales.get(locale);
>+        if(localeIds != null) {
>+            Map entries = (Map)localeIds.get(id);
>+            if(entries != null)
>+              return entries;
>+        }
>+        Locale parentLocale = I18nUtils.getParentLocale(locale);
>+        if(parentLocale == null)
>+            return null;
>+        else
>+            return findEntriesRecursively(id, 
>parentLocale); // Recursive call
>+    }
>+
>+}
>
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
>For additional commands, e-mail: commons-dev-help@jakarta.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message