P.S. Please find the JUnit test code attached.

Ricardo Gladwell wrote:
Eric Pugh wrote:

Hi Ricardo.. Sounds like you are working on something I've been wanting for
a long time!


Of course, I was going to release it anyway so please find the source-code attached. Not sure it belongs in commons-configration API; probably better contributed to the hibernate project. If you can think of any improvements please mail the patches back to me for my own project :)

In otherwords, say I am using a Configuration object in my code, and I do
configuration.getDouble("key");. If getDouble throws an exception then I am
going to have these try/catch cluases all over the place, cluttering the
code. and, I really except getDouble() to allows work. If it doesn't, my
application will just pass it on,not have some sort of fancy if getDouble
fails, then try getString or something weird.


Good point, although I'm still dubious about throwing RuntimeExceptions - those things shoot straight through everything like a silver bullet and can even crash some servlet engines.

From my perspective, I'm not bothered if the Configuration object throws exceptions: I wouldn't catch such exceptions in my web application, instead letting them fly all the way to the exception screen. This way, I can see them occuring as I test my application through the browser.

Obviously, sometimes when configuring your application you just want your configuration to work or keep on working untill if it encounters an errors. However, simply allowing your application to ignore exceptions until they create new exception elsewhere seems like a good way to create hard-to-fix bugs. Surely, it would be better to relay the errors and let the application decide what to do with them?

I think what you can do is just wrap your HibernateException in a
ConfiguratoinRuntimeException and toss that.  JDBCConfiguration should
probably be doning the same thing.


Another alternative would be to have a getExceptions() method for all Configurations which stores exceptions occuring and stores them for later reporting. A good comprimise would be to allow all Configuration objects to have two modes: one where exceptions are thrown as soon as they occur and another one which stores exceptions as I suggested.

Kind regards,
-- Ricardo Gladwell

-----Original Message-----
From: Ricardo Gladwell [mailto:[EMAIL PROTECTED]
Sent: Wednesday, October 06, 2004 12:56 PM
To: Jakarta Commons Developers List
Subject: [configuration] handling exceptions in AbstractConfiguration
implementations


Hi All,

I'm currently developing a sub-class of the AbstractConfiguration
classthat uses Hibernate to access configuration properties
(unimaginatively called Hibernate Configuration). I'm slightly concerned
about the way sub-classes are suposed to handle exceptions:

All the abstract method are defined as not throwing exceptions. All
calls to hibernate, however, throw HibernateExceptions. So, for example,
my implementation of getPropertyDirect calls the hibernate Session.get()
method which can throw an exception.

Looking at your implementation of the DatabaseConfiguration I can see
that it simply consumes SQLExceptions thrown from the JDBC API, logging
the stack trace. However, what if you want to be warned of exceptions
being thrown by the underlying implementation of Configuration?

I notice you already have a nestable ConfigurationException implemented.
Surely, all Configuration methods should indicate they will throw this
exception if they are expected to read/write data?

Also, the AbstractConfiguration class does not describe this contract
(logging all errors throw by underlying framework) or what should be
returned in the event of an error? I assume you should return null
values but this is not described anywhere.

Kind regards,
-- Ricardo Gladwell


------------------------------------------------------------------------

package net.sf.jexus.server.components;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.Iterator;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.jexus.server.data.object.ConfigurationProperty;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.configuration.AbstractConfiguration;

/**
* <p>Hibernate configuation class. Reads configuration infomation from a
* database through the <a href="http://www.hibernate.org/";>Hibernate</a>
* O/R mapping API. Data is stored as name-value pairs, along with the class
* information of data stored, using the mapping* defined by the
* [EMAIL PROTECTED] ConfigurationProperty} POJO hibernate xdoclet directives. Values are
* converted to and from strings using * [EMAIL PROTECTED] org.apache.commons.beanutils.ConvertUtils}.</p>
* * @author <a href="mailto:[EMAIL PROTECTED]">Ricardo Gladwell</a>
*/
public class HibernateConfiguration extends AbstractConfiguration {


    /**
     * Logger for this class
     */
    private static final Log log = LogFactory.getLog(HibernateConfiguration.class);

    /**
     * Reference to the session factory.
     */
    Session session;

/**
* */
class KeyIterator implements Iterator {


        Iterator iterator;
        String last;

        public KeyIterator(Iterator iterator) throws HibernateException {
            this.iterator = iterator;
        }

        /**
         * @see java.util.Iterator#hasNext()
         */
        public boolean hasNext() {
            return iterator.hasNext();
        }

        /**
         * @see java.util.Iterator#next()
         */
        public Object next() {
            ConfigurationProperty config = (ConfigurationProperty)iterator.next();
            String key = config.getName();
            last = key;
            return key;
        }

        /**
         * @see java.util.Iterator#remove()
         */
        public void remove() {
            clearProperty(last);
        }

    }

/**
* */
public HibernateConfiguration(Session session) {
super();
if(log.isTraceEnabled()) log.trace("HibernateConfiguration()");
this.session = session;
}


    /**
     * @see 
org.apache.commons.configuration.AbstractConfiguration#getPropertyDirect(java.lang.String)
     */
    protected Object getPropertyDirect(String key) {
        if(log.isTraceEnabled()) log.trace("getPropertyDirect("+key+")");
        ConfigurationProperty config = null;
        try {
            config = (ConfigurationProperty) 
session.get(ConfigurationProperty.class,key);
        } catch(HibernateException e) {
            log.error("Error reading congfiguration property=["+key+"]",e);
            return null;
        }
        try {
            Class clazz = getClass().getClassLoader().loadClass(config.getType());
            return ConvertUtils.convert(config.getValue(), clazz);
        } catch(ClassNotFoundException e) {
            log.warn("Cannot find class=["+config.getType()+"] for 
property=["+key+"]",e);
        }
        return config.getValue();
    }

    /**
     * @see 
org.apache.commons.configuration.AbstractConfiguration#addPropertyDirect(java.lang.String,
 java.lang.Object)
     */
    protected void addPropertyDirect(String key, Object value) {
        if(log.isTraceEnabled()) log.trace("addPropertyDirect("+key+","+value+")");
        ConfigurationProperty config = new ConfigurationProperty();
        config.setName(key);
        config.setValue(ConvertUtils.convert(value));
        config.setType(value.getClass().getName());
        try {
            Transaction transaction = session.beginTransaction();
            session.save(config);
            transaction.commit();
        } catch(HibernateException e) {
            log.error("Error adding congfiguration property=["+key+"]",e);
        }
    }

    /**
     * @see org.apache.commons.configuration.Configuration#isEmpty()
     */
    public boolean isEmpty() {
        if(log.isTraceEnabled()) log.trace("isEmpty()");
        try {
            List list = session.find("from Configuration");
            return list.isEmpty();
        } catch(HibernateException e) {
            log.error("Error reading keys.",e);
            return true;
        }
    }

    /***
     * @see 
org.apache.commons.configuration.Configuration#containsKey(java.lang.String)
     */
    public boolean containsKey(String key) {
        if(log.isTraceEnabled()) log.trace("containsKey("+key+")");
        return (getPropertyDirect(key) != null);
    }

    /**
     * @see 
org.apache.commons.configuration.Configuration#clearProperty(java.lang.String)
     */
    public void clearProperty(String key) {
        if(log.isTraceEnabled()) log.trace("clearProperty("+key+")");
        ConfigurationProperty config = new ConfigurationProperty();
        config.setName(key);
        try {
            Transaction transaction = session.beginTransaction();
            session.delete(config);
            transaction.commit();
        } catch(HibernateException e) {
            log.error("Error clearing congfiguration property=["+key+"]",e);
        }
    }

    /**
     * @see org.apache.commons.configuration.Configuration#getKeys()
     */
    public Iterator getKeys() {
        if(log.isTraceEnabled()) log.trace("getKeys()");
        try {
            List list = session.find("from Configuration");
            return new KeyIterator(list.iterator());
        } catch(HibernateException e) {
            log.error("Error reading keys.",e);
            return null;
        }
    }

}


------------------------------------------------------------------------

/*
 * Copyright 2004 Ricardo Gladwell
 *
 * 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 net.sf.jexus.server.data.object;

/**
* Hibernate persistance JavaBean encapsulating information about
* a configuration property.
* * @hibernate.class
* table="configuration"
* * @todo Create JUnit test cases.
* @author <a href="mailto:[EMAIL PROTECTED]">Ricardo Gladwell</a>
* @version $Revision: 1.1 $, $Date: 2004/10/05 14:03:55 $
*/
public class ConfigurationProperty {


    /**
     * Name of this configuration property.
     */
    String name;

    /**
     * String representation of the valye of this configuration property.
     */
    String value;

    /**
     * Fully qualified class for this configuration property's type.
     */
    String type;

/**
* Returns the key name for this configuration property.
* * @hibernate.id
* generator-class="assigned"
* * @return Returns the name.
*/
public String getName() {
return name;
}


    /**
     * Sets the key name for this configuration property.
     * @param name The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }

/**
* Returns the value of this property.
* * @hibernate.property
* * @return Returns the value.
*/
public String getValue() {
return value;
}


    /**
     * Sets the value of this property.
     * @param value The value to set.
     */
    public void setValue(String value) {
        this.value = value;
    }

/**
* Returns the fully qualified class name for the type
* of the value for this configuration property.
* * @hibernate.property
* * @return Returns the type.
*/
public String getType() {
return type;
}


    /**
     * Sets the fully qualified class name for the type
     * of the value for this configuration property.
     * @param type The type to set.
     */
    public void setType(String type) {
        this.type = type;
    }
}



------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

/*
 * Copyright 2004 Ricardo Gladwell
 *
 * 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 net.sf.jexus.server.components;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.jexus.server.components.HibernateConfiguration;
import net.sf.jexus.server.data.object.ConfigurationProperty;

import org.jmock.Mock;
import org.jmock.MockObjectTestCase;

/**
 * Test cases for the [EMAIL PROTECTED] HibernateConfiguration} class.
 * @author <a href="mailto:[EMAIL PROTECTED]">Ricardo Gladwell</a>
 * @version $Revision: 1.1 $, $Date: 2004/10/06 09:08:54 $
 */
public class HibernateConfigurationTest extends MockObjectTestCase {

    public final static int ITERATIONS = 3;
    Mock mockSession;

    /**
     * Creates mock session.
     * @see junit.framework.TestCase#setUp()
     */
    protected void setUp() throws Exception {
        super.setUp();
        mockSession = new Mock(Session.class);
    }

    /**
     * Tests that the constructor properly stores session value.
     */
    public void testHibernateConfiguration() {
        Session proxy = (Session) mockSession.proxy();
        HibernateConfiguration config= new HibernateConfiguration(proxy);
        assertEquals("Stored session is not equal to session 
passed.",proxy,config.session);
    }

    /**
     * Tests that an empty configuration successfully returns 
     * <code>true</code> on a [EMAIL PROTECTED] HibernateConfiguration#isEmpty}
     * call.
     */
    public void testEmptyConfigWithIsEmpty() {
        mockSession.stubs().method("find").will(returnValue(new LinkedList()));
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());
        assertTrue("Empty configuration is signalling as non-empty.",config.isEmpty());
    }

    /**
     * Tests that a non-empty configuration successfully returns 
     * <code>false</code> on a [EMAIL PROTECTED] HibernateConfiguration#isEmpty}
     * call.
     */
    public void testNonEmptyConfigWithIsEmpty() {
        List list = new LinkedList();
        ConfigurationProperty property = new ConfigurationProperty();
        list.add(property);
        mockSession.stubs().method("find").will(returnValue(list));
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());
        assertFalse("Non-empty configuration is signalling as 
empty.",config.isEmpty());
    }

    /**
     * Tests [EMAIL PROTECTED] HibernateConfiguration#getPropertyDirect} returns string
     * values stored.
     */
    public void testGetPropertyDirectString() {
        ConfigurationProperty property = new ConfigurationProperty();
        property.setName("key");
        property.setValue("value");
        property.setType(String.class.getName());

        mockSession.stubs().method("get").will(returnValue(property));
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());
        Object value = config.getPropertyDirect(property.getName());

        assertNotNull("Value stored cannot be found in config.", value);
        assertTrue("Value not of the same type stored", value instanceof String);
        assertEquals("Value stored cannot be found in config.", property.getValue(), 
value);
    }

    /**
     * Tests [EMAIL PROTECTED] HibernateConfiguration#addPropertyDirect} stores string
     * values correctly.
     */
    public void testAddPropertyDirectString() {
        ConfigurationProperty property = new ConfigurationProperty();
        property.setName("key");
        property.setValue("value");
        property.setType(String.class.getName());

        Mock mockTransaction = new Mock(Transaction.class);
        mockTransaction.expects(once()).method("commit");

        
mockSession.stubs().method("beginTransaction").will(returnValue(mockTransaction.proxy()));
        mockSession.stubs().method("save");
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());

        mockSession.stubs().method("get").will(returnValue(property));
        config.addPropertyDirect(property.getName(), property.getValue());

        assertNotNull("Value failed to be 
stored.",config.getString(property.getName()));
        assertEquals("Wrong value stored.",config.getString(property.getName()), 
property.getValue());
    }

    /**
     * Tests [EMAIL PROTECTED] HibernateConfiguration#containsKey} correctly verifies
     * if a string value has been stored.
     */
    public void testContainsKeyString() {
        ConfigurationProperty property = new ConfigurationProperty();
        property.setName("key");
        property.setValue("value");
        property.setType(String.class.getName());

        mockSession.stubs().method("get").will(returnValue(property));
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());
        assertTrue("Cannot find property key 
stored.",config.containsKey(property.getName()));
    }

    /**
     * Tests [EMAIL PROTECTED] HibernateConfiguration#clearProperty}
     */
    public void testClearProperty() {
        Mock mockTransaction = new Mock(Transaction.class);
        mockTransaction.expects(once()).method("commit");

        
mockSession.stubs().method("beginTransaction").will(returnValue(mockTransaction.proxy()));
        mockSession.stubs().method("delete");
        mockSession.expects(once()).method("delete");
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());

        config.clearProperty("key");
    }

    /**
     * Tests [EMAIL PROTECTED] HibernateConfiguration#getKeys} correctly iterates 
through all
     * stored properties to correctly retrieve keys. Also tests that iterator
     * removes properties correctly.
     */
    public void testGetKeys() {
        List properties = new LinkedList();
        for(int i = 1; i <= ITERATIONS; i++) {
            ConfigurationProperty property = new ConfigurationProperty();
            property.setName("key"+i);
            property.setValue("value"+i);
            property.setType(String.class.getName());
            properties.add(property);
        }

        mockSession.stubs().method("find").will(returnValue(properties));
        HibernateConfiguration config= new HibernateConfiguration((Session) 
mockSession.proxy());

        Iterator iterator = config.getKeys();
        int i = 0;
        while(iterator.hasNext()) {
            Object value = iterator.next();
            assertTrue("Incorrect object type returned.", value instanceof String);
            String key = (String) value;
            ConfigurationProperty property = (ConfigurationProperty) properties.get(i);
            assertEquals("Incorrect value returned",property.getName(),key);
            i++;
        }

        Mock mockTransaction = new Mock(Transaction.class);
        mockTransaction.expects(once()).method("commit");

        
mockSession.stubs().method("beginTransaction").will(returnValue(mockTransaction.proxy()));
        mockSession.stubs().method("delete");
        mockSession.expects(once()).method("delete");
        iterator.remove();
    }

}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to