PDA

View Full Version : Web Services, AOPProxies, and Sessions (HttpSessionState)


vanccurtis
03-13-2007, 02:43 PM
I have web services using .NET's HttpSessionState, and I'd like to AOP some logging at the web service level (as opposed to lower layers). I seem to be able to do Web Services and Sessions, or Web Services and AOP, but not both together. As soon as I add the I don't think I've seen anywhere in the documentation or examples which actually explicitly says that you can do both, so I'm wondering if I'm trying to do something unsupported or just really backwards! I'm still relatively new to .NET and Spring so I apologize in advance for the newbie-ness of the question.

I've included the web.config file and the mock session service C# code. It's in a standard VS2005 Web Service project, and the only other code dependency I'm using is the ConsoleLoggingAroundAdvice provided in the Spring examples (except I changed the output from the console to a Spring log4Net logger). I'm running my tests using Microsoft's development server from within VS2005. See more notes after files.

* web.config file (note that I've snipped the logging stuff and replaced w/ "..."):

-----------------------------------------------------------------------
<?xml version="1.0"?>

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

<configSections>
<sectionGroup name="common">
<section name="logging"
type="Common.Logging.ConfigurationSectionHandler,
Common.Logging" />
</sectionGroup>
<sectionGroup name="spring">
<section name="context"
type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
<section name="objects"
type="Spring.Context.Support.DefaultSectionHandler,
Spring.Core" />
</sectionGroup>
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"
/>
</configSections>

<system.web>
<httpModules>
<!-- need the WebSupportModule if you're using Spring's WebContextHandler
as opposed to just a ContextHandler above -->
<add name="Spring"
type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
</httpModules>
<httpHandlers>
<add verb="*" path="*.asmx"
type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
</httpHandlers>
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>
<compilation debug="true">
<assemblies>
<add assembly="nunit.framework, Version=2.2.9.0, Culture=neutral,
PublicKeyToken=96D09A1EB7F44A77"/>
<add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<authentication mode="Windows" />
</system.web>

<common>
<logging>...</logging>
</common>

<spring>

<context type="Spring.Context.Support.WebApplicationContext, Spring.Web">
<!-- using section in App.config -->
<resource uri="config://spring/objects"/>
</context>

<objects xmlns="http://www.springframework.net" >
<description>Spring Web Services</description>

<object id="mockVwsSessionServiceTarget"
type="gov.va.medora.vws.facades.MockVwsSessionService">
</object>

<object id="mockVwsSessionServiceProxy"
type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="TargetName" value="MockVwsSessionServiceTarget" />
<property name="InterceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</object>

<object id="MockVwsSessionService"
type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="mockVwsSessionServiceProxy"/>
<property name="Interfaces" value="gov.va.medora.vws.facades.IMockVwsSessionService" />
<property name="Namespace" value="http://vws.medora.va.gov"/>
<property name="Description" value="Sprung Mock Vws Service"/>
</object>

<object id="mockVwsSessionServiceTargetTwo"
type="gov.va.medora.vws.facades.MockVwsSessionService">
</object>

<object id="MockVwsSessionServiceTwo"
type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="mockVwsSessionServiceTargetTwo"/>
<property name="Interfaces" value="gov.va.medora.vws.facades.IMockVwsSessionService" />
<property name="Namespace" value="http://vws.medora.va.gov"/>
<property name="Description" value="Sprung Mock Vws Service"/>
</object>

<!-- aroundAdvice just logs around any command -->
<object id="aroundAdvice"
type="gov.va.medora.vws.ConsoleLoggingAroundAdvice" />
</objects>

</spring>

<log4net>...
</log4net>

</configuration>
-----------------------------------------------------------

* MockVwsSessionServices
------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

using Common.Logging;
using Spring.Context;
using Spring.Context.Support;

namespace gov.va.medora.vws.facades
{
/// <summary>
/// Revisiting Sessions and Web Services and Spring
/// </summary>
/// <remarks> This first attempt uses MS's WebMethod attributs</remarks>
[WebService(Namespace = "http://vws.medora.va.gov")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class MockVwsSessionService : System.Web.Services.WebService, IMockVwsSessionService
{
private static readonly ILog LOG = LogManager.GetLogger(System.Reflection.MethodBase. GetCurrentMethod().DeclaringType);

Boolean connected;

string connectionId;

public MockVwsSessionService()
{
}

/// <summary>
/// Basically create a fake connected state and store it some how...
/// </summary>
/// <param name="someConnection"></param>
/// <returns></returns>
[WebMethod(EnableSession = true)]
public string connect(string someConnection)
{
Session["connection"]="yes";
connectionId = someConnection;
Session["connectionId"] = connectionId;
Connected = true;
return ("Session " + someConnection + " has connection: " + (string)Session["connection"] + " and is Connected: " + Connected);
}

/// <summary>
/// Disconnects from our fake connected state
/// </summary>
/// <returns></returns>
[WebMethod(EnableSession = true)]
public string disconnect()
{
Session["connection"]="no";
Connected = false;
return ("Session " + (string)Session["connectionId"] + " has connection: " + (string)Session["connection"] + " and is Connected: " + Connected);

}


public Boolean Connected
{
[WebMethod(EnableSession = true)]
get { return connected; }
[WebMethod(EnableSession = true)]
set { connected = value; }
}

[WebMethod(EnableSession = true)]
public string isConnected()
{
return ("Session " + (string)Session["connectionId"] + ": Connected: " + Connected + " and has Session: " + (string)Session["connection"]);
}
}

public interface IMockVwsSessionService
{
string connect(string someConnection);

string disconnect();

string isConnected();

Boolean Connected {get; set; }
}
}
-------------------------------------------------------------

When I use the generated pages to look at the target service (i.e. MockVwsSessionServiceTarget) directly, I don't have any problems reading or writing from HttpSessionState in the connect() and disconnect() methods. When I point to the WebServiceExporter (i.e. MockVwsSessionService) then the HttpSessionState breaks. This is the case whether I go through the AOP proxy or not -- see MockVwsSessionService vs. MockVwsSessionServiceTwo.

I'm not using Spring's IApplicationContext in the example above, but it looks like I can only retrieve objects wired in the Spring config file, not actually put objects back into the application context -- true?

Also, using Spring's configuration with MemberAttributes doesn't seem to make a difference.

So, to restate, my goal is to have web services w/ sessions and be able to AOP those web services for logging or other cross-cutting concerns. Any help will be greatly appreciated.

thanks,
van.

Bruno Baia
03-13-2007, 04:52 PM
Hi,

I'm curious, can you try with mockVwsSessionServiceProxy.asmx ?
( Proxies inherits type/method attributes from the target class. )


PS: There is a CommonLoggingAroundAdvice in the Spring Calculator.

Bruno

vanccurtis
03-13-2007, 05:27 PM
Hi Bruno,

I get a 404 when asking for the mockVwsSessionServiceProxy.asmx -- I'm assuming that is because it is not revealed by either Microsoft's Web Services approach, or revealed by Spring's WebServiceExporter. In the web.config I gave you, both MockVwsSessionServiceTarget.asmx and MockVwsSessionServiceTargetTwo.asmx work. The difference between the two examples (as I understand it) is that in the first, I insert an AOP proxy between my target web service and the WebServiceExporter. When I point to either of the WebServiceExporter proxies, I get a null pointer exception trying to use the Session accessors:

---------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at gov.va.medora.vws.facades.MockVwsSessionService.di sconnect() in c:\SANDBOX\vs2005\workspace\VWS\vws\App_Code\main\ csharp\medora\vws\facades\MockVwsServices.cs:line 95
at Spring.Objects.ObjectUtils.InvokeMethod(MethodInfo method, Object instance, Object[] arguments) in j:\release\Spring.Net\src\Spring\Spring.Core\Objec ts\ObjectUtils.cs:line 489
at Spring.Aop.Framework.ReflectiveMethodInvocation.In vokeJoinpoint() in j:\release\Spring.Net\src\Spring\Spring.Aop\Aop\Fr amework\ReflectiveMethodInvocation.cs:line 86
at Spring.Aop.Framework.AbstractMethodInvocation.Proc eed() in j:\release\Spring.Net\src\Spring\Spring.Aop\Aop\Fr amework\AbstractMethodInvocation.cs:line 240
at gov.va.medora.vws.ConsoleLoggingAroundAdvice.Invok e(IMethodInvocation invocation) in c:\SANDBOX\vs2005\workspace\VWS\vws\App_Code\main\ csharp\medora\vws\AOPLoggers.cs:line 101
at Spring.Aop.Framework.AbstractMethodInvocation.Proc eed() in j:\release\Spring.Net\src\Spring\Spring.Aop\Aop\Fr amework\AbstractMethodInvocation.cs:line 263
at Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Inv oke(Object proxy, Object target, Type targetType, MethodInfo targetMethod, Object[] args, IList interceptors) in j:\release\Spring.Net\src\Spring\Spring.Aop\Aop\Fr amework\DynamicProxy\AdvisedProxy.cs:line 149
at Spring.Proxy.CompositionAopProxy_7002f17e37264acca 743d4f66c98a799.disconnect()
at Spring.Proxy.WebServiceProxy_0ee9a6c12fd84d12a1bb1 55f6e77d816.disconnect()

-------

The fun part is that if I start from scratch and do connect, I'm fine, then disconnect gets the error above, then connect gets the same error above (except for the line number of course.)

So, is what I'm trying to do not completely unreasonable?

thanks,
van.

Bruno Baia
03-16-2007, 09:51 PM
Hi,


I get a 404 when asking for the mockVwsSessionServiceProxy.asmx -- I'm assuming that is because it is not revealed by either Microsoft's Web Services approach, or revealed by Spring's WebServiceExporter.

There is 2 ways to export a Web service :

- Register the object that is decorated with the WebServiceAttribute in Spring container.
That's why mockVwsSessionServiceTarget.asmx works.
mockVwsSessionServiceProxy.asmx does not work because there is a bug in AOP proxy generation that does not copy target type attributes. See this post (http://forum.springframework.net/showpost.php?p=5893&postcount=4).
So the aop proxy is not decorated with the WebServiceAttribute.

- Use WebServiceExporter
The problem here is that you are using a property from the base class "System.Web.Services.WebService" from which your web service inherits.
You should use HttpContext.Current.Session instead.


Bruno

Bruno Baia
03-19-2007, 02:27 AM
Hi,

I commited a fix that copy target type attributes to proxy type, so you should be able to call mockVwsSessionServiceProxy.asmx.
but you still need to use HttpContext.Current.Session instead of base.Session when your service inherits from WebService.


so, you got 2 solutions :

- you service is decorated with all needed 'web services' attributes and you don't need to override them by configuration.
You don't need to use WebSErviceExporter.

<object id="mockVwsSessionServiceTarget"
type="gov.va.medora.vws.facades.MockVwsSessionServ ice">
</object>

<object id="mockVwsSessionServiceProxy"
type="Spring.Aop.Framework.ProxyFactoryObject">
<property name="TargetName" value="MockVwsSessionServiceTarget" />
<property name="InterceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</object>



- you service is not tied to a web service (don't inherit from WebService, and no attributes set) and you want to create it by configuration using WebServiceExporter.

<object id="MockVwsSessionService"
type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="mockVwsSessionServiceProxy"/>
<property name="Interfaces" value="gov.va.medora.vws.facades.IMockVwsSessionSe rvice" />
<property name="Namespace" value="http://vws.medora.va.gov"/>
<property name="Description" value="Sprung Mock Vws Service"/>
<property name="MemberAttributes">
<dictionary>
<entry key="*">
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
<property name="EnableSession" value="True"/>
</object>
</entry>
</dictionary>
</property>
</object>

vanccurtis
03-26-2007, 08:27 PM
That does appear to give me what I need at the moment. I'll give the fix a shot when we update our frameworks (hopefully in the near future).

Thanks again, and may your expensive taylor always keep you in style ;-)

van.