PDA

View Full Version : Child object definitions and abstract classes.


Tom Whitner
11-23-2004, 06:19 PM
I have begun working with child object definitions to deal with common configuration across classes of objects. Initially, I mapped the base definitions to the corresponding abstract type, but then realized that this was just coincidence as the object definition really had nothing to do with the existence of the abstract type in my assembly. Regardless, I discovered some issues with the behavior of child object definitions which are dicsussed below.

Section 3.5 of the reference document states:
the container's internal PreInstantiateSingletons method will completely ignore object definitions that don't have either a parent or class attribute set, since they are not complete."

Section 3.5 also states this:
An object definition explicitly can also be declared as abstract... this will prevent the object from being
instantiated (and any attempt to instantiate the object will result in an ObjectDefinitionIsAbstract exception
being thrown). Note also that XmlObjectFactory will by default pre-instantiate all singletons, but singleton
object definitions that have been declared as abstract will not be pre-instantiated.

These two statements seems to contradict each other.

I conducted four tests to verify Spring's behavior.

Given the following two classes:
public abstract class AbstractClass {
public AbstractClass() {
}
private string baseProperty;
public string BaseProperty {
get { return this.baseProperty; }
set { this.baseProperty = value; }
}
}

public class DerivedClass : AbstractClass {
public DerivedClass() {
}
private string childProperty;
public string ChildProperty {
get { return this.childProperty; }
set { this.childProperty = value; }
}
}


Test 1
<object name="AbstractClass" class="abstracts.AbstractClass, abstracts">
<property name="BaseProperty"><value>MyValue</value></property>
</object>

<object name="DerivedClass" class="abstracts.DerivedClass, abstracts" parent="AbstractClass">
<property name="ChildProperty"><value>MyOtherValue</value></property>
</object>

Results:
Error creating object with name 'AbstractClass' defined in '[Configuration File Section] : objects' : Initialization of object failed : Could not instantiate type [Void .ctor()]; is it an interface or an abstract class? Does it have a no-arg constructor?
This result is expected as the class listed is abstract.

Test 2
<object name="AbstractClass">
<property name="BaseProperty"><value>MyValue</value></property>
</object>

<object name="DerivedClass" class="abstracts.DerivedClass, abstracts" parent="AbstractClass">
<property name="ChildProperty"><value>MyOtherValue</value></property>
</object>


Results:
Object definition does not carry a resolved object class
This result contradicts the first statement. The object "AbstractClass" has neither a "parent" not a "class" attribute

Test 3
<object name="AbstractClass" class="abstracts.AbstractClass, abstracts" abstract="True">
<property name="BaseProperty"><value>MyValue</value></property>
</object>

<object name="DerivedClass" class="abstracts.DerivedClass, abstracts" parent="AbstractClass">
<property name="ChildProperty"><value>MyOtherValue</value></property>
</object>


Results:
Error creating object with name 'AbstractClass' defined in '[Configuration File Section] : objects' : Initialization of object failed : Could not instantiate type [Void .ctor()]; is it an interface or an abstract class? Does it have a no-arg constructor?
I expected "True" to be a valid option and Spring didn't complain. The DTD lists "true" and "false" as valid, I would expected all booleans to work (i.e. Any casing of True and False as well as 0 and 1.)

Test 4
<object name="AbstractClass" class="abstracts.AbstractClass, abstracts" abstract="true">
<property name="BaseProperty"><value>MyValue</value></property>
</object>

<object name="DerivedClass" class="abstracts.DerivedClass, abstracts" parent="AbstractClass">
<property name="ChildProperty"><value>MyOtherValue</value></property>
</object>

Results: Sucess! :)

The results of tests 2 & 3 were not consistent with my expectations.

Thanks,
Tom

P.S. Sorry this was so long....

Mark Pollack
11-28-2004, 11:58 PM
Hi,

Thanks for pointing this out. There was a bug for the usage in test case #2 which is now fixed in CVS. I have also updated the documentation. The relevant part now reads

In the case that the parent definition does not specify a class...the parent object can not get instantiated on its own since the definition is incomplete. The definition is also implicitly considered to be abstract. An object definition can also be explicitly declared as abstract using the abstract attribute. Valid values of the attribute are true and false. An abstract definition like this is usable just as a pure template or abstract object definition that will serve as a parent definition for child definitions. Trying to use such parent objects on it's own (by referring to it as a ref property of another object, or doing an explicit GetObject() with the parent object id), will result in an error. Declaring the object as abstract will prevent it being instantiated and any attempt to instantiate the object will result in an ObjectDefinitionIsAbstract exception being thrown. The container's internal PreInstantiateSingletons method will completely ignore object definitions which are considered abstract.

Important note: Application contexts (but not simple object factories) will by default pre-instantiate all singletons. Therefore it is important (at least for singleton objects) that if you have a (parent) object definition whic you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true, otherwise the application context will actually attempt to pre-instantiate it.


About test case #3, the use of values other than "true" or "false" should cause a validation error against the DTD. I've entered this issue as JIRA #13 (http://opensource.atlassian.com/projects/spring/browse/SPRNET-13) for more investigation. As long as there are appropriate error messages, I'm inclined to keep the strict usage of true and false in order to keep the DTD simple.

Cheers,
Mark

Tom Whitner
11-30-2004, 02:43 PM
I agree with keeping the DTD simple, but would it be possible to create a "boolean" type within the DTD? I have only worked with XML Schema, so I'm not sure. The reason I ask is that when you initialize a boolean property it will accept anything that the .NET Boolean class can parse into a boolean value. So, I am free to use "true", "True", and "1" when setting properties on my classes. However, when setting boolean values that control Spring's behavior, I must use "true" only. It just seems a little inconsistent and problematic.

- Tom

Mark Pollack
11-30-2004, 05:17 PM
Hi Tom,

I looked into this and here is what I found. The Boolean.Parse(String) method converts only "true" and "false" strings with case insensitivity. Trying to convert "1" throws a format exception. Convert.ToBoolean(String) delegates to Boolean.Parse(String). Spring currently delgates to this .NET behavior for setting boolean properties. We could loosen this by introducing a TypeConverter for properties that supports 0, 1 and other strings that eval to false, but as you indicate it is better to be consistent with .NET BCL behavior

XML Schema defines boolean to be "true", "false", "0", and "1" and the strings are case sensitive. Using "True" doesn't validate. There isn't a corresponding boolean type in DTD, though it is typically defined as true or false only, not including 0 or 1. We could expand the DTD to use 0 or 1 to be in line with XML Schema, but then it wouldn't correspond to .NET BCL semantics. If we keep it the way it is, the only difference between the DTD and .NET behavior in case sensitivity. Maybe support for common variations like, True and False could be included but it doesn't seem worth the effort imho. Comments?

Cheers,
Mark

Tom Whitner
12-07-2004, 03:57 PM
Unfortunately, as you've demonstrated, consistency is always relative! I wouldn't bother changing it, but it may be worth mentioning somewhere in the help docs or a FAQ.

Thanks,
Tom

Anonymous
12-08-2004, 08:33 PM
As an aside, perhaps the library should use System.Xml.XmlConvert This properly handles the cases of conversion from ValueTypes to/from their corresponding string formats when reading/writing XML. XmlConvert.ToBoolean handles correctly parses "1", "true", "0", "false" semantics.