PDA

View Full Version : Wildcard matching for ASPX/ASCX objectdefinitions?



Erich Eichinger
01-16-2006, 06:18 PM
It has been one of the first ideas I had during writing my first Spring-based Web.

What about allowing entries like

<object type="*.aspx" abstract="true">
...
</object>
? Since all of my pages/controls within a web are derived from a common baseclass anyway, I would like to see something like that. Of course such a wildcard-definition should only match, if no specific match could be found.

Another (more generic) approach could be to investige the inheritance-hierarchy during the Context.Configure() process to find a matching objectdefinition.
Let's say i have a base-class like "MyWeb.MyPageBase". The entry would look

<object type="MyWeb.MyPageBase" abstract="true">
...
</object>
The Context.Configure() process could go through from the most specific type down to the most generic type until a match is found (or not).

I tried to achieve this by deriving from Spring's PageHandlerFactory. But this class is clearly not designed to be inherited, since i can't access the internal types "PageHandler" and "SessionAwarePageHandler". It needs some ugly code to find out, if Spring itself has already found a match, before doing my own stuff.

Since of course there's a performance-penalty searching the inheritance-tree, my solution would be providing 2 different PageHandlerFactories to choose from.

br,
erich

Aleks Seovic
01-19-2006, 04:28 AM
Hi Erich,

I'm not sure what this would buy us that cannot be accomplished using standard object definition inheritance.

You can define parent definitions for different pages within you application using standard Spring.NET features, so I don't quite see the need for another page handler factory.

Can you explain in more detail what you would use this for?

Regards,

Aleks

Erich Eichinger
01-19-2006, 11:56 AM
Hi Aleks,

The reason is simple: I'm lazy ;-)

The details: I'm currently developing a small WebPortal-Framework. My usual implementation strategy is to inject a "Session-Facade" object to all .aspx/.ascx that allows - for example - to read information for the currently selected navigation-item. This is common across all .aspx/.ascx within the application.

In addition, most of the .aspx just displaying information from the database are not implemented by myself but by some Webproducers. And I don't want these Webproducers having to edit the config-files for each page they implement.

The Solution I implemented now:
1) I provide mandatory baseclasses for Controls & Pages
2) I customized the WebApplicationContext and the PageFactory with my own WebObjectFactory which is inheritance-aware

Thus my config file now looks like this:


<objects xmlns="http&#58;//www.springframework.net">

<object id="Web.SessionFacade" type="MyWeb.SessionFacade, MyWeb" scope="request">

</object>

<object id="Web.SessionFacadeAware" abstract="true">
<property name="SessionFacade" ref="Web.SessionFacade" />
</object>


<object type="MyWeb.UI.UserControl, MyWeb" parent="Web.SessionFacadeAware" abstract="true" />


<object type="MyWeb.UI.Page, MyWeb" parent="Web.SessionFacadeAware" abstract="true" />

</objects>


If the WebObjectFactory doesn't find an explicit definition for e.g. "~/somepage.aspx", it goes up the inheritance-tree looking for a definition matching a basetype of this page and - if found - generates an objectdefinition on the fly. internally this results in having the following definition map:


<objects xmlns="http&#58;//www.springframework.net">

&#91;snipped - see above&#93;


<object type="MyWeb.UI.Page, MyWeb" parent="Web.SessionFacadeAware" abstract="true" />


<object type="~/somepage.aspx" parent="MyWeb.UI.Page, MyWeb" abstract="true" />
</objects>



Maybe I'm missing something, but AFAIK using standard Spring.Web features this is not possible.

br,
erich

smhinsey
01-19-2006, 03:37 PM
I may be mistaken, but in your first example, marking your base class as abstract should result in the desired behavior.

Erich Eichinger
01-19-2006, 03:45 PM
I may be mistaken, but in your first example, marking your base class as abstract should result in the desired behavior.

I'm not sure, what you mean. You mean making MyWeb.UI.Page, MyWeb and MyWeb.UI.UserControl, MyWeb abstract in my code?

smhinsey
01-19-2006, 04:01 PM
No, in your definition. I have a similar situation, except what I do is inject properties into both the base page instance and the concrete implementation. The definition looks something like this:

<object scope="request" abstract="true" id="BasePage">

...
<object type="login.aspx" parent="BasePage">
...

As long as your type inherits BasePage, you'll get its dependencies injected, regardless of the presence of a concrete type.

Erich Eichinger
01-19-2006, 04:47 PM
As long as your type inherits BasePage, you'll get its dependencies injected, regardless of the presence of a concrete type.

Of course. As long as you're talking about definition-inheritance, this is true.

Maybe I didn't make my intention completely clear. What I want to do is:

1) Imlement a base class



public abstract class MyBasePage &#58; Spring.Web.UI.Page
&#123;
private string sampleValue;

public string SampleValue &#123;
set &#123; sampleValue = value; &#125;
get &#123; return sampleValue; &#125;
&#125;
&#125;

public abstract class MyDerivedPage&#58; MyBasePage
&#123;
// whatever
&#125;


2) Insert an Object-Definition into the spring.config



<object type="MyBasePage, MyAssembly" abstract="true" >
<property name="SampleValue" value="injected value" />
</object>



3) Write a sample.aspx



<%@ Page Inherits="MyDerivedPage" %>
<%=SampleValue%>



4) Don't need to add an objectdefinition for "~/sample.aspx" to the spring.config, since there can be many .aspx pages in our webprojects.

Spring should detect, that sample.aspx is derived from MyBasePage which has an objectdefinition and apply this "MyBasePage"-objectdefinition.

I hope, I could make things clearer now.

br,
erich

Aleks Seovic
01-19-2006, 07:19 PM
Hi Erich,

Thanks for the detailed example, it is much clearer now what is it that you are trying to accomplish.

I don't think custom page handler factory and dynamically created object definitions are the best solution in your case. It's certainly one of the alternatives, but I think there is a simpler one (and in my world, simpler is better).

Basically, your case is somewhat specific because you want to limit Spring DI to a certain level of the inheritance hierarchy, namely your base page and user control classes. What you are trying to do is ensure that those things indeed get injected for every page that inherits your base class, so in my opinion, it is the class itself that should provide that guarantee, not the page handler factory.

What you can do is this:

1. Modify your base page and control definitions to have explicitly defined id attribute (you should do this regardless of the approach), say for example, "Web.Page" and "Web.UserControl".

2. Add the code to your base page class that will inject dependencies based on the template defined in step 1:



protected override void OnInit&#40;EventArgs e&#41;
&#123;
ApplicationContext.ConfigureObject&#40;this, "Web.Page"&#41;;
base.OnInit&#40;&#41;;
&#125;


3. Add similar code to your base user control class.

This approach will ensure that required dependencies are injected, but it will also allow users to create more specific object definitions for each page, possibly injecting additional dependencies, without having to specify your base definition as a parent.

Regards,

Aleks

Erich Eichinger
01-19-2006, 07:33 PM
Thanks for the detailed example, it is much clearer now what is it that you are trying to accomplish.

Sorry - next time, I'll try to come to the point much earlier...

Thank's for this really simple solution. I didn't think of that. (Ok - I'm working with spring for 4 days now - couldn't expect that)

There's just one thing with this solution, that bothers me: Your solution forces me to somewhat "hardcode" DI into my code. That's something I wanted to avoid. Even if I read the definition name ("Web.Page" in this case) from some other location, it breaks the clear and straightforward configuration-process of spring. I tried to find a solution that's more generic, but staying compatible with the way, objects are configured in spring.

br,
erich

Aleks Seovic
01-19-2006, 07:48 PM
What you are saying is entirely true, but it shouldn't be a problem in your case.

You said that you are building a portal framework on top of Spring.NET, in which case it is perfectly fine that your portal has a requirement that Web.Page and Web.UserControl definitions have to exist.

It is in a way similar to how we use different configuration sections, such as spring/context, spring/typeAliases, etc. Or, to give you an even better example, it is exactly they way we deal with global message sources -- by using a well-known object definition with an id of 'messageSource'.

You still get all the benefits of DI -- your template can define what exactly gets plugged into your pages and controls, and you can have many different pluggable implementations of the SessionFacade. You are only making more explicit a dependency that your framework has anyway.

Later,

Aleks

Erich Eichinger
01-24-2006, 06:33 PM
Hi Aleks,

I now switched to your suggested solution.


protected override void OnInit&#40;EventArgs e&#41;
&#123;
ApplicationContext.ConfigureObject&#40;this, "Web.Page"&#41;;
base.OnInit&#40;&#41;;
&#125;

There's one issue here: If nothing's injected into the page before OnInit() is called, there's also no ApplicationContext ...

One solution is to hardcode a call to WebApplicationContext.Current, but wouldn't it be much nicer, if the PageHandlerFactory at least checked a page for the IApplicationContextAware interface and inject the context?

br,
erich

Aleks Seovic
01-24-2006, 09:26 PM
You are absolutely right, I'll make the necessary change in the page handler factory.

Thanks,

Aleks