PDA

View Full Version : Legacy Singleton


Ted Husted
05-12-2005, 05:16 PM
I'm trying to wrap a legacy singleton in Spring, as described here:

http://www.springframework.net/doc/reference/html/objects.html#objects-factory-class-static-factory-method

But, when I try it, my test comes back


TestCase 'WNE.Core.Commands.CountyTest.CountyKeys' failed: Spring.Objects.Factory.ObjectCreationException : Error creating object with name '(inner object)' defined in 'file [C:\projects\Nexus\wne2\Test\Resources\Command\Cat alog.xml] line 21' : Initialization of object failed : Object definition does not carry a resolved object class
----> System.ApplicationException : Object definition does not carry a resolved object class


The singleton reads a configuration file (iBATIS) and created a service object.

Right now, I'm calling it from a property on a base class:


private SqlMapper _Mapper;
public SqlMapper Mapper
{
get
{
// return _Mapper;
return IBatisNet.DataMapper.Mapper.Instance ();
}
set
{
_Mapper = value;
}

}


But I would prefer to inject it.

Here's the XLM that I tried:

[code]
<object id="Mapper" type="IBatisNet.DataMapper.Mapper, IBatisNet.DataMapper" factory-method="Instance" >
</object>

<object id="county_key_list" type="WNE.Core.Commands.BaseKeyList, WNE.Core">
<property name="Mapper"><object ref="Mapper"/></property>
</object>
[code]

Any clues would be appreciated.

-Ted.

Mark Pollack
05-13-2005, 06:16 AM
Hi Ted,

I think you used "object ref" instead of "ref object" in the XML. (You must be getting tired :) ) We have been working on improving the error messages - sorry it wasn't clear what was going on.

To help reduce XML errors install the Spring.NET schema into VS.NET (http://www.springframework.net/doc/reference/html/vsnet.html) or whatever XML editor you are using. Reversing the tags like you did in VS.NET gave little red squiggles.

Here is the definition that worked for me

<objects xmlns="http://www.springframework.net" >

<object name="myPerson" type="ibatisplay.Person, ibatisplay">
<property name="Name"><value>Mark</value></property>
<property name="SqlMapper"><ref object="SqlMapper"/></property>
</object>

<object id="SqlMapper" type="IBatisNet.DataMapper.Mapper, IBatisNet.DataMapper"
factory-method="Instance"/>

</objects>

Cheers,
Mark

Rick Evans
05-13-2005, 08:12 AM
Hi Ted

Yeah, as you mentioned on this post (http://forum.springframework.net/viewtopic.php?t=109), one doesn't need any special integration stuff to use 3rd party libraries such as NHibernate and iBatis.NET... everything can be wired up using the stuff that's already available. But I guess its like having a drink on a wet lazy Sunday afternoon in the height of summer, when its so hothothot your tongue hangs out just like a hungry dog's does when he's eyeing a juicy rack of lamb... a glass of ice cold water sure is nice (and hits the spot), but an ice cold beer is just a whole lot sweeter.

When I'm back in commit mode (shortly), you'll be able to wire up your SqlMapper instance(s) using configuration like this (this first snippet uses the iBatis defaults for the name of the config file, its location, etc)...

<object id="mySqlMapper" type="Spring.Data.IBatis.SqlMapperFactoryObject, Spring.IBatis"/>

If you need access to more than one SqlMapper instance (different databases?), you'll also be able to write your config like so...

<object id="ArtFairSqlMapper" type="Spring.Data.IBatis.SqlMapperFactoryObject, Spring.IBatis">
<property name="ConfigFile"><value>file://d:/bench/ArtFairSqlMapperConfig.xml</value></property>
</object>
<object id="BarSqlMapper" type="Spring.Data.IBatis.SqlMapperFactoryObject, Spring.IBatis">
<property name="Config"><value>file://d:/bench/BarSqlMapperConfig.xml</value></property>
</object>

There's also support for configuring and using the DAO abstraction offered by the iBatis.DataAccess library, and being able to hook into transaction contexts using Spring's IPlatformTransactionManager abstraction. And well, yeah, that's about it.

On a side note Ted, since you are 'iBatis.NET guy', are you actually using the abstractions provided by the iBatis.DataAccess library? I ain't, 'cos the iBatis.DataMapper library (and its SqlMapper class) provide all the functionality I need. Just wonderin'...

Ciao
Rick

Shucks, all this talk of ice cold water and ice cold beer sure has made me thirsty. I'm off to go grab a cup of tea. Yeah I know, I'm living life on the edge here :?

Ted Husted
05-13-2005, 11:51 AM
I think you used "object ref" instead of "ref object" in the XML. (You must be getting tired :) ) We have been working on improving the error messages - sorry it wasn't clear what was going on.


Thanks, Mark. That fixed it. :oops:

-Ted.

Ted Husted
05-13-2005, 12:00 PM
On a side note Ted, since you are 'iBatis.NET guy', are you actually using the abstractions provided by the iBatis.DataAccess library? I ain't, 'cos the iBatis.DataMapper library (and its SqlMapper class) provide all the functionality I need. Just wonderin'...


Well, Gilles Bayon is really the "iBatis.NET guy". I've just been helping out a bit with the documentation, and, oh yeah, the joining Apache thing :)

A while back, Clinton Begin suggested that we drop the DAO framework, since so many people were using other solutions (e.g. Spring), having our own now seemed redundant.

Gilles suggested that we keep it, since, at the time, there was nothing like it in the .NET world.

Of course, times they are a changing, and once SpringNet is ready for a stable release, we could revisit the issue.

And, no, I don't use the iBATIS DAO framework anymore, for either Java or .NET.

-Ted

Ted Husted
05-13-2005, 02:35 PM
Hey, speaking of iBATIS:

The iBATIS config has a section where we can specify whatever individual mapping XML documents we need to load. The mapping documents are the direct equivalent of the spring-object documents.

Do we already have a bootstap loader that would let us list an arbitrary number of spring-object documents? We're test-driven and need to load the objects into multiple projects. Right now, we're creating an array progmaticatically and passing that to XmlApplicationContext. Cheesey, but it's getting us through the day. :wink:

We're using Spring to inject nearly a hundred business objects already, with more on the way. (Our business layer uses a decorator pattern. There's a handful of base objects, which are being decorated by Spring to run different iBATIS statement and what not.)

I'd like to start catagorizing the objects into different files, for sanity's sake, and it would be nice to have an iBATIS-style bootstrapper.

On the subject, support for iBATIS-style type aliases would also be nifty. (Though, you can accomplish much the same thing with parents.)

-Ted.

Rick Evans
05-13-2005, 03:54 PM
Hiya

Well, there is the <import/> element that you can use in XML config based files.

<objects>
<import resource="file://d:/bench/daos.xml"/>
<import resource="http://husted.com/views.xml"/>
<import resource="assembly://WNE.Core/WNE.Core.Commands/commands.xml"/>
</objects>

Is this what you're looking for, or something else entirely? Although I think that at least one <object/> element is required within an <objects/> element.

Mmm, indeed, one object (http://www.springframework.net/doc/reference/html/springobjectsxsd.html) is required...

<xs:element name="object" type="vanillaObject" minOccurs="1" maxOccurs="unbounded" />

No support for type aliases as of yet... file a JIRA feature request though and we'll see how many votes it gets. I may be going down the whole grandmother and the sucking eggs route here, but you can use aliases such as 'int', 'bool', 'char', 'string' etc wherever a primitive type needs to be expressed.

Ciao
Rick

Ted Husted
05-13-2005, 06:26 PM
Well, there is the <import/> element that you can use in XML config based files.

<objects>
<import resource="file://d:/bench/daos.xml"/>
<import resource="http://husted.com/views.xml"/>
<import resource="assembly://WNE.Core/WNE.Core.Commands/commands.xml"/>
</objects>

Is this what you're looking for, or something else entirely?


Almost. :D

So, to load the iBATIS mappers in a test project, we can put the master configuration in the assembly directory ($PROJECT/bin/Debug), but the resource references are to the project root, rather than the location of the configuration file.


<sqlMaps>
<sqlMap resource="/Resources/Query/default.xml"/>
<sqlMap resource="/Resources/Query/County.xml"/>

<sqlMap resource="/Resources/Query/Ticket.xml"/>
</sqlMaps>


The project-relative references means we can use the same master configuration for both the web project and the NUnit project unchanged.

Most of the day-to-day is not in the master configuration, but the XML documents it loads (County.xml, Ticket.xml), so usually these are the only ones we need to keep conformed. Of course, it's helpful if the imported XMLs are in the same relative place in both the NUnit and Web projects.

We'd like to do the same thing with the Spring XMLs. The import hint was helpful, since we were able to drop a bootstrap Objects.xml into each project.


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">

<object id="Base" />

<import resource="Resources/Command/AppConfig.xml"/>
<import resource="Resources/Command/Catalog.xml"/>

</objects>



On the ASP.NET side, this loads just fine from the Web.config


<spring>
<context type="Spring.Context.Support.WebApplicationContext, Spring.Web">
<resource uri="~/Objects.xml"/>
</context>
</spring>



On the NUnit side, we can load it just fine with a custom singleton loader, but it would be better to do it through the App.config, if we can.

We're just having trouble doing the same with the App.config. When, we try, the reference comes up null.



<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>

<spring>

<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="~/Objects.xml"/>
</context>

</spring>

</configuration>


We had the same problem with iBATIS to begin with, but Gilles fixed the configuration loader so that we could use project-relative references for the resources.

Here's the singleton that we use for the NUnit project now (adapted from the iBATIS loader):


public class Objects
{

private static string FILE = "/Objects.xml";

private Objects ()
{
// private constructor prevents instantiation.
}

private static string _rootDirectory =
AppDomain.CurrentDomain.BaseDirectory.Replace (@"\bin", "").Replace (@"\Debug", "").Replace (@"\Release", "");

private static volatile IApplicationContext _Factory = null;

public static IApplicationContext Factory ()
{
if (_Factory == null)
{
lock (typeof (XmlObjectFactory))
{
string foo = "file://" + _rootDirectory + FILE;
if (_Factory == null) // double-check
_Factory = new XmlApplicationContext (foo);
}
}
return _Factory;
}
}


In the base NUnit test, we can load our (IApplicationContextAware) business controller easily enough.


[SetUp]
public virtual void SetUp ()
{
IApplicationContext factory = Objects.Factory ();
_Controller = factory.GetObject (CONTROLLER_ID) as IController;
}



But it would be sweet if we could use the App.config instead.


IApplicationContext factory
= ConfigurationSettings.GetConfig("spring/context") as IApplicationContext;


But, when we try, no joy, the factory comes up null. :(

-Ted.