PDA

View Full Version : Application Context refreshes and con-current access


javajunky
06-05-2007, 08:42 AM
Hi,
I've recently moved from a CVS cut fairly soon after PR3 to 1.1M1, and the changes to the ApplicationContext have burned me somewhat. [not complaining, when following a CVS cut one expects breaking changes]. However one of the changes was fairly fundamental and some of my code (apparently) relies on the older behaviour.

Effectively when a Refresh() occurs on a ConfigurableApplicationContext now, the internal ObjectFactory is set to 'null'. Previously I don't believe this was the case.
however, in my system I have a mixture (eek!) of Web-requests and con-current seperately launched worker threads that 'interact' with the ApplicationContext [this is glue code, the majority of the APP uses DI and knows nothing of the context] .

Effectively the first http-request that comes into the app causes an ApplicationContext refresh to be fired [this is because the spring configuration for an app-context actually gets delivered over the wire, but I need a minimal boot-strapping configuration in place to initiate this transfer.... ].

This in itself, although perhaps a little un-sightly isn't a problem as far as I can tell (at least hasn't been until very recently).

But with the latest version of the Spring, any concurrent calls into the ApplicationContext whilst it is mid-refresh cause NullReferenceExceptions to be fired at the following point in the code:
Spring.Context.Support.AbstractApplicationContext. GetObjectDefinition(String name, Boolean includeAncestors) +18

Now this isn't a problem particularly in the code I've got that is expecting this behaviour I can check the 'nullness' of the ObjectFactory property before requesting a spring object from it, but now I've hit a problem in Spring's PageHandlerFactory.GetHandler code, that I can't work around-

[NullReferenceException: Object reference not set to an instance of an object.]

Spring.Context.Support.AbstractApplicationContext. GetObjectDefinition(String name, Boolean includeAncestors) +18

Spring.Web.Support.PageHandlerFactory.GetHandler(H ttpContext context, String requestType, String url, String physicalPath) +523

System.Web.HttpApplication.MapHttpHandler(HttpCont ext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig) +175

System.Web.MapHandlerExecutionStep.System.Web.Http Application.IExecutionStep.Execute() +120

System.Web.HttpApplication.ExecuteStep(IExecutionS tep step, Boolean& completedSynchronously) +155


My question is, are Refresh'es of the WebApplicationContext no-longer supported, as I can't see how it can work with the current code, and could we not put a simple 'if(this.ObjectFactory == null) return null;' check into the current GetObject definition, rather than throwing null-pointers? Or is this very un-spring ?

Sorry to be a bother !

edit: After posting this I've realised I can just override GetObject in my [already overridden] BootStrappEnabledWebApplicationContext, but I still think it may be an issue for others ??

edit: Actually I'm wrong, java != c# so I can't override the methods :(

Erich Eichinger
06-05-2007, 09:43 PM
Hi,

unfort. Refresh() is not supported by WebApplicationContext since refreshing configuration sections is not supported by HttpRuntime. Thus we decided to drop this feature.

To maybe help out with your problem: When and how is your configuration pulled over the wire? Why doesn't e.g. a resource reference like "http://configserver/myobjects.xml" help?


<spring>
<context>
<resource url="http://configserver/myobjects.xml" />
...


also loads the configuration from somewhere else using the http protocol.

And what do your worker-threads do? You should create standard XmlApplicationContext instances for these worker-threads. If you need "cross-thread-singletons" you may e.g. register existing objects from your WebApplicationContext with the new context instance and pass this new XmlApplicationContext instances to the worker-thread. You shouldn't pass WebApplicationContext instances to your thread as the underlying WebObjectFactory relies on an existing HttpRequest instance in HttpContext.Current.Request. This isn't the case on your worker thread!


cheers,
Erich


-Erich

javajunky
06-05-2007, 10:55 PM
First, thanks very much for getting back to me!

Hi,

unfort. Refresh() is not supported by WebApplicationContext since refreshing configuration sections is not supported by HttpRuntime. Thus we decided to drop this feature.

Eek! Oh no, I've been relying on this functionality for the last few months!.

In several places my application exists in two different stages, for example
*Before database configuration, the spring context only contains a few beans, sufficient for configuring a database
* After database configuration, the spring context contains many beans, including database related beans such as SessionFactory instances.

How would you recommend I achieve this functionality, as currently I load an initial configuration, when the database is setup/tested correctly, the context is refreshed and contains the real app ( for one of my web tiers)

My long term intentions were to be able to modify the context at runtime, to effectively re-wire my app depending on load (OSGI style), so I'd been running under the assumption that Context refreshes were going to be allowed :(. Whilst it is true that the Web.config might not be changeable, it isn't neccessarily true that other resources couldn't change( for example your example of a remote xml file ) ? This changes my app considerably, doh!




To maybe help out with your problem: When and how is your configuration pulled over the wire? Why doesn't e.g. a resource reference like "http://configserver/myobjects.xml" help?


<spring>
<context>
<resource url="http://configserver/myobjects.xml" />
...


also loads the configuration from somewhere else using the http protocol.

My configuration is loaded from somewhere else over a call to a web service i.e. http://configserver/ConfigurationService.asmx . Where the web-service request is parameterised. Currently I do the load asynchronously after the context has originally loaded a basic (boot-strapping context), so I can present a 'loading aspx file' to the client whils the configuration comes down the wire, do you think I could support this by creating my own 'resource' handler?


And what do your worker-threads do? You should create standard XmlApplicationContext instances for these worker-threads. If you need "cross-thread-singletons" you may e.g. register existing objects from your WebApplicationContext with the new context instance and pass this new XmlApplicationContext instances to the worker-thread. You shouldn't pass WebApplicationContext instances to your thread as the underlying WebObjectFactory relies on an existing HttpRequest instance in HttpContext.Current.Request. This isn't the case on your worker thread!


Eek! Eek! Eek! I have a lot of threads operating con-currently on the ApplicationContext, in my naivety I assumed that as long as the objects that these worker threads were operating on had come from the context then everything would be ok (it is possible this means my other post on PROPAGATION_REQUIRES_NEW is meaningless! :( ).

The ApplicationContext that the worker threads operate on would/should contain exactly the same spring definitions as the WebApplicationContext, can you suggest an approach I can take programmatically to simplify this duplication somewhat ? ...At the minute I'm just spawning new threads, and injecting in the existing singleton references that the class instance which creates the thread already had references onto. I'm not 100% on how I would achieve programatically what you're proposing other than duplicating the context manually, for every thread I launch in the system ?

I'm very sorry for the complexity of this post, this problem is something that I need to get right urgently! :)

Erich Eichinger
06-05-2007, 11:17 PM
Hi,

I decided to split the issues into multiple posts now ;-)


Oh no, I've been relying on this functionality for the last few months!.

Sorry - in this case I guess I mixed up things. I was talking about ContextRegistry.Clear()...
Of course you should be able to call Refresh() on an application context. I don't know, what has happened here. Mark has refactored some parsing stuff right before M1. Maybe there's some lock missing now. I added JIRA issue SPRNET-583 (http://opensource.atlassian.com/projects/spring/browse/SPRNET-583) for this.

you could try adding a lock( aSyncObject ) around your Refresh() calls.

-Erich

javajunky
06-05-2007, 11:21 PM
Hi,

I decided to split the issues into multiple posts now ;-)


Sorry - in this case I guess I mixed up things. I was talking about ContextRegistry.Clear()...
Of course you should be able to call Refresh() on an application context. I don't know, what has happened here. Mark has refactored some parsing stuff right before M1. Maybe there's some lock missing now. I added JIRA issue SPRNET-583 (http://opensource.atlassian.com/projects/spring/browse/SPRNET-583) for this.
Phew!!! You will never know how much you just scared me!!! The easiest fix as far as I can tell would just be to return null if ObjectFactory == null in the getobject calls.

Incidentally having these as virtual would have been handy to change the behaviour, or at least modify it in my own Context implemens ? I [now] understand that C# is closer to C++ in the way it supports polymorphism rather than Java, but is the trade-off in speed for making a method non-virtual worth it ?

Unfortunately I can't use a 'lock' to mutex around the issue as the exception I see comes from within the SpringPageHandlerFactory (all my code is correctly locked <g>!). I've tried copy+pasting the whole class to fix it manually but I ran into a load of package scoping issues, so short of patching the cvs code I can't sort it locally :(

Erich Eichinger
06-05-2007, 11:27 PM
have a lot of threads operating con-currently on the ApplicationContext

do you really need the appcontext on this threads? I'd strongly recommend to avoid passing WebApplicationContexts. Instead I suggest you do maybe a GetObject() call and pass the returned object(s) to the worker thread - without anymore need for an appcontext there.

You could write a helper class, that contains all objects required by your workerthreads and configure/populate this object right before spawning the thread.

Erich Eichinger
06-05-2007, 11:30 PM
I've hit a problem in Spring's PageHandlerFactory.GetHandler code, that I can't work around-

[NullReferenceException: Object reference not set to an instance of an object.]

Spring.Context.Support.AbstractApplicationContext. GetObjectDefinition(String name, Boolean includeAncestors) +18

Spring.Web.Support.PageHandlerFactory.GetHandler(H ttpContext context, String requestType, String url, String physicalPath) +523

System.Web.HttpApplication.MapHttpHandler(HttpCont ext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig) +175

System.Web.MapHandlerExecutionStep.System.Web.Http Application.IExecutionStep.Execute() +120

System.Web.HttpApplication.ExecuteStep(IExecutionS tep step, Boolean& completedSynchronously) +155



Could you maybe run your code with a debug build of spring to guide me to the exact source line of this exception?

tx,
Erich

Erich Eichinger
06-05-2007, 11:43 PM
do you think I could support this by creating my own 'resource' handler?

Imho this is a cleaner solution, yes.

A nice solution could also be to leverage WebRequest.RegisterPrefix() to register your own protocol + protocol handler (=WebRequest derived class) and register a standard UrlResource for your custom protocol with ResourceHandlerRegistry.

This is rather easy to accomplish and allows e.g. for writing:


<spring>
<context>
<resource uri="myprotocol:/someuri-pointing-to-my-config" />
...


I'm not sure, which of those methods is easier to implement. In any case, the "InputStream" (or "GetResponse()" in case of WebRequest) could spawn an async download when called for the first time and return only a dummy/bootstrap configuration instead. After download has finished, the implementation could call Refresh() on the app context or ContextRegistry.Clear() (which empties the ContextRegistry and forces all context instances to be recreated (for webapps I recommend that method)

hope this helps,
Erich

javajunky
06-06-2007, 08:33 AM
do you really need the appcontext on this threads? I'd strongly recommend to avoid passing WebApplicationContexts. Instead I suggest you do maybe a GetObject() call and pass the returned object(s) to the worker thread - without anymore need for an appcontext there.

You could write a helper class, that contains all objects required by your workerthreads and configure/populate this object right before spawning the thread.

This is in-fact what I do, sorry I guess there was some confusion here? There aren't any calls to 'CurrentContext.GetObject()' anywhere. The threads operate on service beans/objects that are injected into them by the code that spawns the threads, is this ok ? [My worries are around the TransactionSynchroniser, in Spring.Data]

javajunky
06-06-2007, 08:37 AM
Could you maybe run your code with a debug build of spring to guide me to the exact source line of this exception?

tx,
Erich
I don't think need to tbh, I can see the problem from a brief eye-ball of the code,
I would be fairly certain the exception is being thrown from line 114:


IObjectDefinition pageDefinition = (appContext != null) ?appContext.GetObjectDefinition(appRelativeVirtual Path, true) : null ;


If the App context is mid-refresh when that code executes, the AppContext's call to GetObjectDefinition atttempts to use an ObjectFactory property that is currently null. (As the AbstractXMLContext sets it to null during the Refresh call)

Hope this helps ?

Erich Eichinger
12-16-2007, 08:14 PM
Hi,

just to close this thread: calls to ApplicationContext.Refresh() are properly synchronized since 20th July.

-Erich