PDA

View Full Version : Interception in Prototypes


Zoetermeer
10-26-2006, 05:43 PM
Is it true that one of the limitations of AOP (with Spring.NET) is that you can't reliably do interception for method calls on prototypes? I know that this was an issue with the Java version of Spring - with AspectJ I could do this, but I know AspectJ was actually plugging into the compilation process to do its weaving there. I know that in Java, Spring couldn't monitor the lifetime of a prototype after it was created by the application context.

I guess basically what I'm asking is how to do runtime weaving with Spring.NET for prototypes. I'd like to do this with business object instances - it would be much cleaner than what I'm currently doing, which is using the ContextBoundObject - message sink - proxy approach.

Also - from playing around with Spring.NET, it looks like you aren't able to do interception for property accessors (getters/setters), even though these are actually compiled into methods. The documentation says you aren't able to do field interception - but was ambiguous as to whether this means fields as in a member variable, such as:

public string name;

As opposed to:

public string Name
{
get { ... }
set { ... }
}

If so this would be disappointing, since you are able to intercept property getters/setters with the ContextBoundObject approach.

Thanks in advance!

Bruno Baia
10-27-2006, 02:20 AM
Hi,

both features are supported in Spring.NET

- You can apply aspects on prototypes using the PropertyTargetSource :

<object id="someService" type="SpringSample.Service.SomeService, SpringSample" singleton="false" />

<object id="someServiceWeaved" type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="targetSource">
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
<property name="TargetObjectName" value="someService" />
</object>
</property>
<property name="interceptorNames">
<list>
<value>AnAdviceHere</value>
</list>
</property>
</object>


- and you can also intercept getters/setters which are, in fact, methods.


Cheers,
Bruno

ptd
10-31-2006, 10:51 PM
Hi Bruno,

For the current project I'm working on we are using Spring AOP framework to intercept method calls on a number of prototypes. 90% of the time it's fine but during some load testing we noticed a number of exceptions being thrown by Spring:


Exception type: Spring.Objects.Factory.ObjectCreationException
Source: Spring.Core
Message: Error creating object with name '' : IFactoryObject threw exception on object creation.
Stack trace: at Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObjectForSharedInstance(String name, Object instance)
at Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name, Type requiredType, Object[] arguments)
at Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name, Object[] arguments)
at Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObject(String name)
at Spring.Context.Support.AbstractApplicationContext. GetObject(String name)
....

The inner exception details:
Exception type: System.NullReferenceException
Source: Spring.Aop
Message: Object reference not set to an instance of an object.
Stack trace: at Spring.Aop.Framework.DynamicProxy.CompositionProxy Builder.BuildProxy(IAdvised advised)
at Spring.Aop.Framework.DynamicProxy.DynamicProxyMana ger.GetProxy(AdvisedSupport advised)
at Spring.Aop.Framework.DefaultAopProxyFactory.Create AopProxy(AdvisedSupport advisedSupport)
at Spring.Aop.Framework.AdvisedSupport.CreateAopProxy ()
at Spring.Aop.Framework.ProxyFactoryObject.NewPrototy peInstance()
at Spring.Aop.Framework.ProxyFactoryObject.GetObject( )
at Spring.Objects.Factory.Support.AbstractObjectFacto ry.GetObjectForSharedInstance(String name, Object instance)

Exception type: System.NullReferenceException
Source: Spring.Aop
Message: Object reference not set to an instance of an object.
Stack trace: at Spring.Aop.Support.NameMatchMethodPointcut.Matches (MethodInfo method, Type targetType)
at Spring.Aop.Framework.AdvisorChainFactoryUtils.Calc ulateInterceptors(IAdvised config, Object proxy, MethodInfo method, Type targetType)
at Spring.Aop.Framework.HashtableCachingAdvisorChainF actory.GetInterceptors(IAdvised advised, Object proxy, String methodId, MethodInfo method, Type targetType)
at Spring.Aop.Framework.DynamicProxy.BaseCompositionP roxy.GetInterceptors(Type targetType, String methodId, MethodInfo method)


I extracted part of our application into a simple console application to test this scenario. Here is my config file:


<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="AOPAdvice" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor" singleton="false">
<property name="MappedName" value="Execute"/>
<property name="Advice">
<object type="TestConsole.Spring.AOP.AOPAdvice, TestConsole">
</object>
</property>
</object>

<object id="ProxyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="proxyInterfaces" value="TestConsole.Spring.ITask" />
<property name="isSingleton" value="false"/>
<property name="targetName" value="RealObjectPrototypeTarget" />
<property name="interceptorNames">
<list>
<value>AOPAdvice</value>
</list>
</property>
</object>

<object id="RealObjectPrototypeTarget" type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop" singleton="false">
<property name="TargetObjectName">
<idref object="RealObjectTarget"/>
</property>
</object>

<object id="RealObjectTarget" type="TestConsole.Spring.Tasks.TestTask, TestConsole" singleton="false"/>
</objects>


Here is the code for my main class:


class Program
{
static void Main(string[] args)
{
// Initialise Spring first
InitialiseSpring();

// Spawn 5 threads, each thread executing the TestProxyObject() method
List<Thread> testThreads = new List<Thread>();
for (int i = 0; i < 5; i++)
{
testThreads.Add(new Thread(TestProxyObject));
}

// Execute the threads
for (int i = 0; i < 5; i++)
{
testThreads[i].Start();
}
}

private static void TestProxyObject()
{
ITask task = ContextRegistry.GetContext("Test").GetObject("ProxyObject") as ITask;
task.Execute();
}

private static void InitialiseSpring()
{
ContextRegistry.Clear();
IApplicationContext context = new XmlApplicationContext("assembly://TestConsole/TestConsole/Spring.Spring.config.xml");
ContextRegistry.RegisterContext("Test", context);
}
}


I've got a number of Console.WriteLine() statements which output a local integer variable in the class:


>>> AOPInvoke (6)
>>> AOPInvoke (7)
>>> TestTask (7)
>>> TestTask (6)
TestTask (6) = 1
<<< TestTask (6)
AOPAdvice = (6) = 1
<<< AOPInvoke (6)
TestTask (7) = 1
<<< TestTask (7)
AOPAdvice = (7) = 1
<<< AOPInvoke (7)


The number in the brackets is the CurrentThread.ManagedThreadId. The application crashes soon after the exception above is displayed.

If from my main class I call TestProxyObject() explicitly (ie. not delegated to a thread):


static void Main(string[] args)
{
InitialiseSpring();

TestProxyObject();
TestProxyObject();
TestProxyObject();
TestProxyObject();
TestProxyObject();
TestProxyObject();
}


It works fine. Console output:


>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)
>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)
>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)
>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)
>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)
>>> AOPInvoke (1)
>>> TestTask (1)
TestTask (1) = 1
<<< TestTask (1)
AOPAdvice = (1) = 1
<<< AOPInvoke (1)


Any ideas? Is it a problem with my spring config xml or something more disturbing? :)

Bruno Baia
10-31-2006, 11:17 PM
Hi,

I guess you are using 1.0.2 version or somehting older than august 2006.

In this case, can you test using one of the latest nightly build (http://www.springframework.net/downloads/nightly) plz ?
Otherwise I will as you provided everything to do it :D

Thanks for catching this,
Bruno

ptd
11-01-2006, 01:05 AM
Thanks for the quick reply!!

I tried using the build Spring.NET-20061025-2219 and it seemed to work:


>>> AOPInvoke (6)
>>> AOPInvoke (5)
>>> AOPInvoke (4)
>>> TestTask (6)
>>> TestTask (4)
TestTask (4) = 1
<<< TestTask (4)
AOPAdvice = (4) = 1
<<< AOPInvoke (4)
TestTask (6) = 1
<<< TestTask (6)
AOPAdvice = (6) = 1
<<< AOPInvoke (6)
>>> TestTask (5)
TestTask (5) = 1
<<< TestTask (5)
AOPAdvice = (5) = 2
<<< AOPInvoke (5)
>>> AOPInvoke (7)
>>> TestTask (7)
>>> AOPInvoke (3)
>>> TestTask (3)
TestTask (3) = 1
<<< TestTask (3)
AOPAdvice = (3) = 3
<<< AOPInvoke (3)
TestTask (7) = 1
<<< TestTask (7)
AOPAdvice = (7) = 1
<<< AOPInvoke (7)


The only comment I have is I would have expected my prototype Advice class to always output 1?

Bruno Baia
11-02-2006, 11:43 PM
The only comment I have is I would have expected my prototype Advice class to always output 1?

Hmmm, weird...
What does your AOPAdvice exactly ?

- Bruno

ptd
11-03-2006, 12:04 AM
Here is the code from my advice class:


public class AOPAdvice : IMethodInterceptor
{
int i = 0;

#region IMethodInterceptor Members

/// <summary>
/// Invokes the specified invocation.
/// </summary>
/// <param name="invocation">The invocation.</param>
/// <returns></returns>
public object Invoke(IMethodInvocation invocation)
{
// Proceed with task execution
Console.WriteLine(">>> AOPInvoke (" + Thread.CurrentThread.ManagedThreadId + ")");
object returnObject = invocation.Proceed();
i++;
Console.WriteLine("AOPAdvice = (" + Thread.CurrentThread.ManagedThreadId + ") = " + i);
Console.WriteLine("<<< AOPInvoke (" + Thread.CurrentThread.ManagedThreadId + ")");
return returnObject;
}

#endregion
}

Bruno Baia
04-16-2007, 11:33 AM
FYI : related thread :
http://forum.springframework.net/showthread.php?t=2450

Bruno