View Full Version : Dynamic class instantiation
twalker
07-03-2005, 06:55 AM
I am new to Spring.NET so forgive me if this question is simple.
I am trying to use Spring's new NT service to call a MessageController assembly. When a
jms message comes in I would like to call a component that is defined in Spring on an IProcessMessage
interface. I can have the IProcessMessage interface referenced in my MessageController assembly,
however I do not want to have to reference the component the MessageController is going to call.
I would like to use Spring to help me make this call dynamically using the IProcessMessage interface.
Is this possible in Spring.NET?
Here is the sample config file I have constructed?
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net" >
<description>A client example that demonstrates a local call</description>
<object name="TestBusinessObj" type="SampleBusinessObj.SampleBusinessObj, SampleBusinessObj">
</object>
</objects>
</spring>
The following code works as long as I have SampleBusinessObj referenced in my project. But I don't want to have to reference the
component in my project. I would like to call the component dynamically.
// Spring.NET Call
IApplicationContext ctx = (IApplicationContext)ConfigurationSettings.GetConf ig("spring/context") as IApplicationContext;
IProcessMessage myBusObj = (IProcessMessage)ctx["TestBusinessObj"];
myBusObj.ProcessMessage(message, properties);
Any help would be greatly appreciated!
Thanks
Rick Evans
07-03-2005, 12:21 PM
Hiya
In a word... yes. This is one of the benefits of interface based programming that Spring.NET facilitates. Your code and configuration as described in your post will work (from what I can see).
Let’s says you have an assembly called MessageProcs that contains interfaces only. One of the interfaces it contains is called (as per your example) IProcessMessage (I have taken some liberties with your interface since you don’t describe it in depth)...
using System;
using System.Collections;
namespace MessageProcs {
public interface IProcessMessage
{
void ProcessMessage(object message, IDictionary properties);
}
}
You have another, separate assembly called SampleBusinessObj (as per your example). It contains a class (SampleBusinessObj, in the SampleBusinessObj namespace) that implements the IProcessMessage interface. This assembly needs (of course) to reference the MessageProcs assembly.
using System;
using System.Collections;
using MessageProcs;
namespace SampleBusinessObj
{
public class SampleBusinessObj : IProcessMessage
{
public void ProcessMessage(object message, IDictionary properties)
{
Console.WriteLine(message);
}
}
}
You have another, separate assembly called MessageController (as per your example). It contains one or more classes that use the IProcessMessage interface. It doesn't care (or need to know) what the implementation of the IProcessMessage interface is, it simply needs the interface; so it too needs (of course) to reference the MessageProcs assembly (but not the SampleBusinessObj assembly). Find below a very simple example...
using System;
using System.Collections;
using System.Configuration;
using MessageProcs;
using Spring.Context;
namespace MessageController
{
public class SimpleMessageController
{
public void SendMessage(object message)
{
IApplicationContext ctx = ConfigurationSettings.GetConfig("spring/context") as IApplicationContext;
IProcessMessage myBusObj = (IProcessMessage) ctx["TestBusinessObj"];
myBusObj.ProcessMessage(message, new Hashtable());
}
}
}
So what you want to do is wire up the SampleBusinessObj class (which is a concrete implementation of the IProcessMessage interface), to the SimpleMessageController. As per your code (more on this later), the SimpleMessageController gets a reference to an IApplicationContext and pulls 'some' IProcessMessage implementation instance out of the context. It doesn’t know exactly what implementation, and it doesn’t care. The exact implementation is configured in the context configuration file...
<?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>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<description>A client example that demonstrates a local call.</description>
<object name="TestBusinessObj"
type="SampleBusinessObj.SampleBusinessObj, SampleBusinessObj"/>
</objects>
</spring>
</configuration>
This works... the SampleBusinessObj assembly is not referenced by either the MessageProcs assembly or the MessageController assembly. This is what you wanted to know right? The SampleBusinessObj assembly can simply be 'dropped' into the same directory as the application (if you are doing a rich client), or it can be dropped into the 'bin' directory of your web apps virtual directory if you are doing an ASP.NET application; it can also be strongly named and installed into the GAC too (you will have to sign the MessageProcs assembly too in this case)... Spring.NET will locate your assembly and plug it in 'dynamically for you. There's no magic here, just plain old .NET assembly resolution going on under the covers.
Finally :) ... the example code I have described here is not the best way to get the most bang for your buck out of Spring.NET (and IoC in general); I simply tried to best answer the question at hand. The SimpleMessageController class would be better off being configured in the context itself (maybe you can't do this, I don’t know), so that it can have its dependency on an IProcessMessage instance supplied by the container. To wit...
<?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>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<description>A client example that demonstrates a local call.</description>
<object name="TestBusinessObj"
type="SampleBusinessObj.SampleBusinessObj, SampleBusinessObj"/>
<object name="MessageController"
type="MessageController.SimpleMessageController, MessageController">
<property name="MessageProcessor">
<ref object="TestBusinessObj"/>
</property>
</object>
</objects>
</spring>
</configuration>
With the SimpleMessageController class being changed like so; yeah you have to make a change to your code, but its for the better (your mileage may vary - I daresay you know your specific business requirements a whole lot better than me, so I'll leave it with you), 'cos your SimpleMessageController class is now much easier to test, and doesn't have a whole lot of Spring.NET specific dependencies and lookup code in it anymore.
using System;
using System.Collections;
using System.Configuration;
using MessageProcs;
using Spring.Context;
namespace MessageController
{
private IProcessMessage _messageProcessor;
public class SimpleMessageController
{
public void SendMessage(object message)
{
_messageProcessor.ProcessMessage(message, new Hashtable());
}
public void MessageProcessor
{
set
{
_messageProcessor = value;
}
}
}
}
Of course, you might want to have the SimpleMessageController class implement an interface, so that it too can be wired in to your other classes with a minimum of dependencies :)
Hope this answers your question... if not, just bang away on that 'Post Reply' button.
Have a kicking the-rest-of-your-weekend, ciao
Rick
twalker
07-03-2005, 08:30 PM
Rick,
Thanks for taking the time to reply to my post. I have this sample working. However, for some reason when I pull the SampleBusinessObj assembly out of my MessageController project and try to have Spring.NET call it dynamically it gives me an error and says that my config.file is in error. I have also double checked to make sure that the SampleBusinessObj dll is in the same directory as the MessageController. Any thoughts on this anomaly?
Thanks
Rick Evans
07-03-2005, 08:36 PM
Hiya
Can you post your config file on this thread? And the layout of your project?
Actually, better yet, I'm deep in the bowels of a Spring.NET session right now, so get me while I'm hot :) Feel free to mail me your config file and any associated files (in confidence of course)... if you can that'd be easiest.
Oh yeah, my email address is uncleelvis _AT_ gmail.com. I think it's also listed on my 'profile' which will be somewhere next to my name after I post this (I think).
Ciao
Rick
twalker
07-03-2005, 08:45 PM
Rick,
I found my problem. I must be asleep. When I pulled the reference to the Business Obj out of my project it deleted from my bin directory. I put it back in and everything is working fine now. Thanks for all of your help! Have a good rest of the weekend.
Thanks!
Rick Evans
07-03-2005, 08:53 PM
Cool!
Hey, I did the same thing a few weeks back... I kept on going 'it builds fine in NAnt... so what is going on with Visual Studio? ... what! aww, dow!'
I must've been asleep too :wink:
Ciao
Rick
This post was brought to ya'll on the back of Rilo Kiley's 'My Slumbering Heart'.
Mark Pollack
07-06-2005, 03:38 PM
Hi,
I have been writing messaging based middleware for quite a few years now and we (codestreet) have ported our mini "messaging container" from Java to .NET that seems to be along the lines of what you are doing. I figure it would be good to describe it to you - see if you might find it useful - it has been invaluable to us putting order on complex messaging apps. Although, this is more for server side infrastructure, we ended up using it on our .NET frontend. BTW, we are using TIBCO's JMS as the provider btw, what are you using?
I'll post here the config file that we use to structure the processing for you to get the general idea. I'm pretty sure we can put a version of the code in our cvs sandbox. Essentially ou have a collection of handlers....
<handler name="TraderAxeHandler" classname="CodeStreet.Application.Reactor.RMsgHandler">
<reactor>JmsReactor</reactor>
<exceptionModule>DefaultExceptionModule</exceptionModule>
<selector>Workflow = 'TraderAxe'</selector>
<requiredMessageClasses>
com.codestreet.agora.message.ServerTraderAxeCreate ,
com.codestreet.agora.message.ServerTraderAxeUpdate ,
(Additional data types here ...)
</requiredMessageClasses>
<lock>Header.OrderID</lock>
<discardRedelivered>false</discardRedelivered>
<pipeline name="TraderAxe">
<module name="TraderAxeManager"/>
(Additional modules here...)
</pipeline>
</handler>
The reactor element picks the implementation of the messaging service that will deliver async events. The name of the reactor corresponds to the name of an object configured in the spring context. The name Reactor itself refers to the POSA Reactor pattern. (http://www.cs.wustl.edu/~schmidt/POSA/) (Just to be overly academic about it :)). The handler class is responsible for converting the incoming JMS message to a normal C# object. We could have used Spring itself to configure the entire handler but we had these XML config stuff already in place. Without custom namespace support in Spring - coming soon - this is approach is certainly less verbose.
At this point in the callback flow you declare the conditions by which an incoming message will be passed to an processing pipeline. The pipeline is composed of modules that are essentially a calling chain of classes that implement something similar to the IProcessMessage class mentioned previously.
The selection criteria are based on the message content and also by the System.Type that the incoming message gets converted to. The selection based on message content lets you write the same SQL selector syntax as in the JMS spec but performs the selection on a MapMessage - not the headers. We implemented our own (extremely efficient) parser for this. You can perform the SQL on any object really - MapMessage, HashTable, ordinary objects with properties. This is very powerful because you don't want to keep sticking information into the JMS headers all the time.
The other selection option is based on the System.Type that the incoming message object was converted to. We use a reflection based converter to perform this conversion.
You can then ask for a coarse gained lock over all the message processing based on the property of the incoming object. This gives you a handle on ordering of related messages while still having a pool of worker threads. Basically all your create/update/delete for a given Order will be processed in the correct sequence.
Now you get to the real message processing, which as I mentioned before is a chained collection of processing modules. The names of these modules refer to the names of objects in the spring context and get wired up w/ their dependencies such as DAO, Caches, etc.
An exception thrown that escapes a processing module will get caught by the declared exception module. This lets you centralize your exception logic.
I'll put this on my long list of things to-do for placement in the cvs sandbox...
Cheers,
Mark
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.