PDA

View Full Version : Referencing collection entries or defining property placeholders programmatically


dolcept
04-06-2007, 09:29 PM
If I have a standalone xml config file which defines two objects. One is a collection--an IDictionary<string, string> to be exact. Is there a way I can reference the collection's entries from another object?

For instance, say I want to set the second object's ConnString property to an entry in the commonConnectionStrings Dictionary:

(all types have correct aliases set up)


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<object id="commonConnectionStrings" type="CommonConnectionStrings">
<property name="['MY_DATABASE']" value="Data Source=blah;Initial Catalog=blah..." />
<property name="['YOUR_DATABASE']" value="Data Source=blah;Initial Catalog=blah..." />
</object>

<object id="reportTaskQueue" type="ReportTaskQueue" singleton="false">
<property name="ConnString" expression="commonConnectionStrings['MY_DATABASE']" />
<property name="Timeout" expression="1 * 60 * 60" />
<property name="RetryCount" value="2" />
</object>
</objects>


I've tried just about every syntax I could find like:

<property name="ConnString" expression="commonConnectionStrings['MY_DATABASE']" />
<property name="ConnString" expression="${commonConnectionStrings}['MY_DATABASE']" />
<property name="ConnString" expression="${commonConnectionStrings['MY_DATABASE']}" />
<property name="ConnString" expression="@(commonConnectionStrings['MY_DATABASE'])" />
<property name="ConnString" expression="@(spring.root:commonConnectionStrings['MY_DATABASE'])" />


...(tried using ref and value attributes too) and get various errors with all of them. I've read about the PropertyPlaceholderConfigurer that would allow me to use the "${expression}" syntax, but that doesn't work when using a standalone xml file containing only <objects>. I can not use an "app.config" file for my app, and need to instantiate an IApplicationContext like so:

IApplicationContext ctx = new XmlApplicationContext(configFile);


Is it possible to reference collection entries within an xml config file like this, or is it possible to define some properties programmatically prior to instantiating the IApplicationContext as shown above--similarly to how you can define Type aliases with the TypeRegistry class before creating the XmlApplicationContext? That way, perhaps the ${prop.name} syntax would work?

There's nowhere to define PropertyPlaceholderConfigurer in a standalone xml file since the xml schema used by the XmlApplicationContext only expects a single root <objects> tag.

Ideas, workarounds?

--Paul

Mark Pollack
04-07-2007, 02:15 AM
Hi Paul,

There seems to be an issue using expressions that contain spring object references within the configuration file. We'll look into this. However, I think using a PropertyPlaceholderConfigurer is better for your use case. The PropertyPlaceholderConfigurer can read a seperate configuration file that looks very much like what you would find in a standard app.config (nested config sections are not supported) with NameValueSectionHanders. Here is the boilerplate XML


<object name="appConfigPropertyHolder"
type="Spring.Objects.Factory.Config.PropertyPlaceholderC onfigurer, Spring.Core">

<property name="location" value="file://NameValues.xml"/>
<property name="configSections" value="ConnectionStrings"/>

</object>

<object name="reportTaskQueue"
type="FactoryExample.ReportTaskQueue, FactoryExample">

<property name="ConnString" value="${MY_DATABASE}"/>

</object>



and NameValues.xml looks like


<?xmlversion="1.0"encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ConnectionStrings" type="System.Configuration.NameValueSectionHandler, System" />
</configSections>

<ConnectionStrings>
<add key="MY_DATABASE" value="Data Source=my_blah;Initial Catalog=blah..."/>
<add key="YOUR_DATABASE" value="Data Source=your_blah;Initial Catalog=blah..."/>
</ConnectionStrings>
</configuration>


Just be sure that NameValues.xml is in the same directory as the application exe. This will let you keep the admin like configuration data external to the main context config file. If for some reason you want them all in the same place, you could use PropertyPlaceholderConfigurer property, named Properties, i.e.


<property name="Properties">
<name-values>
<add key="PROD_DATABASE" value="Data Source=prod_blah;Initial Catalog=blah..."/>
</name-values>
</property>


Generally speaking using 'Properties' is to provide default values that get overriden by the other external files. Look at the SDK docs for other usage scenarios.

There is also some new functionality for property replacement using classes that implement IVariableSource. For example PropertyFileVariableSource reads a Java style property file and there are others that read from the registry and environment variables. Docs for that are forthcoming.

I'd like to collect the cases when people don't use the standard app.config, what is the reason you need to avoid its use?

Cheers,
Mark

dolcept
04-07-2007, 06:46 AM
Excellent response, Mark...I can't wait to get to work Monday to try this out.

To make a long story short, I'm creating a generic task processor that pulls jobs out of multiple, configurable queues and asynchronously runs them in multiple, configurable thread pools.

The first reason for having an external config file is that due to the nature of the application environment that the task processor will be deployed in, we need to be able to use it in two modes--one as a standalone console app that can be run at a scheduled time, and secondly as a service that continually executes jobs as they appear in the queues.

We've separated the core, generic features of the task processor into a dll and wrote two exe front-ends that use it. One, a console app, and two, a service exe.

The config for both of these modes will be the same in most situations and using a single config file would help our deployment and maintenance--instead of having two app.config files that have to be kept in sync. This way, the operations guys can deploy the task processor in whichever mode best suits their situation, and there's only one source of config info. In essence, the config file configures the objects in the shared dll, which doesn't get an app.config file by default. Technically, I could create one for the dll, but I'm not sure the .NET Configuration classes that Spring undoubtedly uses will recognize it.

Secondly, due to some overzealous auditors, we're now obligated to encrypt all database connection strings using AES-256. Yick. Fortunately we already have code that performs this encryption/decryption on nodes in an XML file according to the W3C Encrypted XML specs.

Our plan was to store the connection strings in some common area of the config file which could then be turned into Encrypted XML. The PropertyPlaceholderConfigurer examples you provided would be perfect for this.

I thought I could then extend the XmlApplicationContext class to support Encrypted XML files, or implement an IResource (?) that could do it.

It didn't look like the ContextRegistry.GetContext() method handled app.config files that contained Encrypted XML, even though .NET 2 does provide support for them. I couldn't find any way to pass the necessary public/private keys used to perform the decryption to Spring.

Using a standalone config file, I could handle the decryption myself and Spring would be none the wiser.

I suppose a third reason is to assist our QA team and allow them to pass the name of the config file as a command line param or a service param. Heck, that could even be handy in normal, production use as well.

Perhaps a fourth reason is that I come from a Java background, and it just seems more natural this way. :)

Only being able to have a single <objects> tag in a standalone config file is a little limiting...although with the information you provided, it may work perfectly well for our situation.

Thank you for a very well written response. I will, of course, let you know how it goes.

--Paul

Mark Pollack
04-07-2007, 02:53 PM
Hi Paul,

Glad you are on your way.

Technically, I could create one for the dll, but I'm not sure the .NET Configuration classes that Spring undoubtedly uses will recognize it.

That should work, we do it all the time for Spring's test assemblies that are run through nunit.

I thought I could then extend the XmlApplicationContext class to support Encrypted XML files, or implement an IResource (?) that could do it.

Yea, I believe you had a post on this that Aleks' responded to, that is one way to go as well.

It didn't look like the ContextRegistry.GetContext() method handled app.config files that contained Encrypted XML, even though .NET 2 does provide support for them. I couldn't find any way to pass the necessary public/private keys used to perform the decryption to Spring.


I've made a JIRA (http://opensource.atlassian.com/projects/spring/browse/SPRNET-519) entry to look into support for this. Note, that one of the IVariableSource implementations supports reading from the ConnectionSettings class, so that might help, but its sounds like you have your own encryption working well. There is also a CommandLineArgsVariableSource. You could override values from the property file w/ those provided on the command line to add some flexibility.

Only being able to have a single <objects> tag in a standalone config file is a little limiting...although with the information you provided, it may work perfectly well for our situation.
You can import other config files from the main one and/or provide multiple resource locations when creating the XmlApplicationContext. See section 4.15 of the docs. You may also be interested in making the context config file an embedded resource so people can't accidentially muck with it.

Cheers,
Mark

Erich Eichinger
04-11-2007, 12:06 AM
Hi,

I'm not exactly sure atm but as far as I remember the expression syntax should be


expression="@(commonConnectionStrings)['MY_DATABASE']"


First @(variableName) resolves to the object where afterwards the []-operator will be evaluated against.

cheers,
Erich