View Full Version : ASP.NET "Atlas" web services integration
Bruno Baia
05-01-2006, 06:48 PM
Hi,
Both Spring.NET and Atlas have its own custom .asmx handler.
The goal of this "third party integration" project is to provide an new handler factory that allow both Spring and Atlas web services to work together.
So now, you can use Spring.NET web services, that enable DI and AOP, with Atlas's controls, that use a generated javascript proxy.
To use it, replace the Atlas .asmx handler by the one provide in Spring.Web.Atlas :
<add verb="*" path="*.asmx" type="Spring.Web.Atlas.Services.ScriptHandlerFactory" validate="false"/>
You can get the project and the associated example from the latest nightly build :
http://www.springframework.net/downloads/nightly/
directory : "sandbox/Integrations/Spring.Web.Atlas"
-Bruno
lahma
11-07-2006, 09:36 AM
Hi,
It seems that the latest version of Microsoft's ASP.NET AJAX (beta 2) breaks the integration. ScriptHandlerFactory is now internal so the integration glue code doesn't compile anymore.
So you cannot inherit anymore from that class, maybe standard implementation of IHttpHandlerFactory and more aggressive reflection?
-Marko
tbroyer
11-07-2006, 10:56 AM
So you cannot inherit anymore from that class, maybe standard implementation of IHttpHandlerFactory and more aggressive reflection?
I've started doing it yesterday on Beta 1 (I wasn't even aware that beta 2 was out ;)) and I think I managed to do it, but there's another problem: WebServiceData's constructor raises an InvalidOperationException with message "Only types with a [ScriptService] attribute can be called as a web service", and there don't seem to be a mean of bypassing it...
Here's my code:
using System;
using System.Web;
using Microsoft.Web.Script.Services;
using System.Reflection;
using System.Web.Caching;
using Spring.Util;
using Spring.Context;
using Spring.Context.Support;
namespace Spring.Web.Script.Services
{
public class ScriptHandlerFactory : IHttpHandlerFactory
{
static private readonly Type scriptHandlerFactoryType =
typeof(ScriptServiceAttribute).Assembly.GetType("Microsoft.Web.Script.Services.ScriptHandlerFactory");
static private readonly FieldInfo scriptHandlerFactory_webServiceHandlerFactory =
scriptHandlerFactoryType.GetField("_webServiceHandlerFactory", BindingFlags.NonPublic | BindingFlags.Instance);
static private readonly Type webServiceDataType =
typeof(ScriptServiceAttribute).Assembly.GetType("Microsoft.Web.Script.Services.WebServiceData");
static private readonly ConstructorInfo webServiceData_ctor =
webServiceDataType.GetConstructor(BindingFlags.Non Public | BindingFlags.Instance, null, new Type[] { typeof(Type), typeof(bool) }, null);
private readonly IHttpHandlerFactory scriptHandlerFactory =
(IHttpHandlerFactory)Activator.CreateInstance(scri ptHandlerFactoryType, true);
public ScriptHandlerFactory()
: base()
{
scriptHandlerFactory_webServiceHandlerFactory.SetV alue(
this.scriptHandlerFactory,
new Spring.Web.Services.WebServiceHandlerFactory());
}
#region IHttpHandlerFactory Members
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string filename = VirtualPathUtility.ToAbsolute(context.Request.File Path);
object webServiceData = context.Cache.Get(filename);
if (webServiceData == null) {
string serviceName = WebUtils.GetPageName(url);
IApplicationContext appContext = WebApplicationContext.Current;
if (appContext.ContainsObjectDefinition(serviceName)) {
Type serviceType = appContext.GetType(serviceName);
webServiceData = webServiceData_ctor.Invoke(new object[] { serviceType, false });
context.Cache.Insert(filename, webServiceData, new CacheDependency(filename));
}
}
return this.scriptHandlerFactory.GetHandler(context, requestType, url, pathTranslated);
}
cref="System.Web.IHttpHandler"/> object to reuse.</param>
public void ReleaseHandler(IHttpHandler handler)
{
/* nothing to do */
}
#endregion
}
}
I'll install beta 2 and see if anything has changed to help us.
tbroyer
11-07-2006, 03:27 PM
I'll install beta 2 and see if anything has changed to help us.
Nothing has changed. I think the solution is to either:
change WebServiceExporter's GetTypeAttributes to call the "base" implementation (making use of ClassAttributes; I actually don'tunderstand why it's not done this way) and inject a ScriptService attribute using the Spring context's configuration
use an Atlas-specific WebServiceExporter that would automatically add the ScriptService attribute along with the WebService one. It would probably also have to automatically add ScriptMethod attribute if one is not set in the configuration (via the Methods property).I've gone this second way and it seems to work. Actually, the sample works, but calling the web-service without the "/js" suffix causes the following exception to be raised:
Both Void AddAdvice(Int32, AopAlliance.Aop.IAdvice) and Void AddAdvice(AopAlliance.Aop.IAdvice) use the message name 'AddAdvice'. Use the MessageName property of the WebMethod custom attribute to specify unique message names for the methods.The ScriptHandlerFactory has not changed, except for the Cache.Insert where the CacheDependency instanciation will throw an error (passing a virtual path). I've just removed the use of a CacheDependency, as there's no *.asmx file to depend on. Actually, I also changed the namespace to Spring.Web.Atlas.Script.Services.
Here's the code of my Atlas-specific WebServiceExporter (I removed comments and many unchanged parts from Spring.Web.Services.WebServiceExporter to shorten it a bit):
namespace Spring.Web.Atlas.Script.Services
{
public class WebServiceExporter : IInitializingObject, IObjectFactoryAware, IFactoryObject, IObjectNameAware
{
#region Fields
[...]
#endregion
#region Constructor(s) / Destructor
static WebServiceExporter()
{
AssemblyName an = new AssemblyName();
an.Name = "Spring.Atlas.WebServices";
assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
module = assembly.DefineDynamicModule(an.Name, false);
}
public WebServiceExporter()
{ }
#endregion
[...]
#region WebServiceProxyTypeBuilder inner class implementation
private sealed class WebServiceProxyTypeBuilder : CompositionProxyTypeBuilder
{
[...]
protected override IList GetTypeAttributes(Type type)
{
IList attrs = new ArrayList();
object[] webServiceAttrs = TargetType.GetCustomAttributes(typeof(WebServiceAt tribute), true);
if (webServiceAttrs.Length > 0) {
// Copy the attribute from the target class
attrs.Add(ReflectionUtils.CreateCustomAttribute((W ebServiceAttribute)webServiceAttrs[0]));
} else {
// Creates a default one based on the exporter properties.
attrs.Add(ReflectionUtils.CreateCustomAttribute(de faultWebServiceAttribute));
}
attrs.Add(ReflectionUtils.CreateCustomAttribute(ty peof(ScriptServiceAttribute)));
return attrs;
}
protected override IList GetMethodAttributes(MethodInfo method)
{
IList attrs = base.GetMethodAttributes(method);
bool containsWebMethodAttribute = false;
bool containsScriptMethodAttribute = false;
foreach (Attribute attr in attrs) {
if (attr is WebMethodAttribute) {
containsWebMethodAttribute = true;
} else if (attr is ScriptMethodAttribute) {
containsScriptMethodAttribute = true;
}
if (containsWebMethodAttribute && containsScriptMethodAttribute) {
break;
}
}
// Creates default WebMethodesAttribute if not set by configuration
if (!containsWebMethodAttribute) {
attrs.Add(ReflectionUtils.CreateCustomAttribute(ty peof(WebMethodAttribute)));
}
// Creates default ScriptMethodAttribute if not set by configuration
if (!containsScriptMethodAttribute) {
attrs.Add(ReflectionUtils.CreateCustomAttribute(ty peof(ScriptMethodAttribute)));
}
return attrs;
}
#endregion
}
#endregion
}
Bruno Baia
11-07-2006, 10:18 PM
Hi,
I was waiting for a good reason to move it to the new Spring.Net.Integration CVS module, now I got it :)
In fact, the name update (Atlas -> Ajax) is already a reason to do it, but now it breaks the integration...
Thanks Marko for catching this, and merci Thomas ;) for the investigation & code, I'll take a look this week and post back here to keep you informed.
- Bruno
tbroyer
11-08-2006, 10:28 AM
and merci M. Royer ;) for the investigation & code
M. Broyer please, and de rien M. Baia ;)
- Thomas
Bruno Baia
11-15-2006, 12:14 AM
Hi Thomas,
sorry about the misspelling last time, I'll use your first name from now as I know it :)
I started to investigate this...
change WebServiceExporter's GetTypeAttributes to call the "base" implementation (making use of ClassAttributes; I actually don'tunderstand why it's not done this way) and inject a ScriptService attribute using the Spring context's configuration
You right, TypeAttributes, but also MemberAttributes (methods property in WebServiceExporter) and Interfaces property should be available in our exporters.
We just never need it before, but today a lot of framework are extending default web services (WSE2, WSE3, Atlas) and yesterday another forum user asked for it (http://forum.springframework.net/showthread.php?t=831), so we implemented it.
We also renamed "methods" property to "MemberAttributes" to be more consistent.
For now, you can use the WebServiceExporter :
<!-- Exports contact service (weaved or not) as a web service. -->
<object id="ContactWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<!--<property name="TargetName" value="ContactService"/>-->
<property name="TargetName" value="ContactServiceWeaved"/>
<property name="Namespace" value="http://Spring.Examples.Atlas/ContactService"/>
<property name="Description" value="Contact Web Services"/>
<property name="TypeAttributes">
<list>
<object type="Microsoft.Web.Script.Services.ScriptServiceAttribu te, Microsoft.Web.Extensions"/>
</list>
</property>
<property name="Interfaces">
<list>
<value>IContactService, App_Code</value>
</list>
</property>
</object>
We can also think to a ScriptServiceExporter which inherits from WebServiceExporter but needs some investigation...
Actually, the sample works, but calling the web-service without the "/js" suffix causes the following exception to be raised
This is because we are proying an AOP proxy which implements specific Spring.Aop interfaces (IAdvised and IAopProxy). Those interfaces will be proxied unless we specify ourself which interfaces to proxy using the Interfaces property.
I'm currently working on this, to remove the need of setting Interfaces property when exporting Aop proxies because it's not intuitive for the end user...
About the ScriptHandlerFactory, which is now internal :( , seems to work as a "ScriptService" but if you access the old .asmx page, it's not working here.
I didn't go deeper, I'll take a look later.
About the Atlas integration, I will move it to "Spring.Web.Extensions" I think.
I will also change the example because it uses AutoCompleteExtender that is not under Microsoft.Web.Extensions (ASP.NET AJAX 1.0 beta2) but under the Microsoft.Web.Preview.dll (ASP.NET 2.0 AJAX Futures November CTP).
I'm being crazy with Microsoft's CTP/beta releases...
Any ideas for the example are welcome...
Bruno,
Bruno Baia
11-16-2006, 10:45 AM
I'm currently working on this, to remove the need of setting Interfaces property when exporting Aop proxies because it's not intuitive for the end user...
Fixed in the last nightly build.
No need to specify interfaces to proxy when exporting an "Aop proxy" with the WebServiceExporter.
Bruno
tbroyer
11-16-2006, 11:03 AM
Thanks a lot Bruno, I'll test it asap.
byoungman
11-27-2006, 02:08 PM
Bruno,
Any idea when this will be finished. I am currently evaluating spring.net for the framework of a web application that my client wants to develop and they would like to use both Spring.Net & Ajax.
Thanks,
Bill
Bruno Baia
12-01-2006, 10:14 AM
Hi,
I'll try to get this working to be avalaible with 1.1 P3, which will be released this week-end.
It will be available separately from the sandbox.
Bruno
Bruno Baia
12-02-2006, 11:17 PM
Hi,
I've updated the code.
You can get it from the sandbox ("sandbox\Integrations\Spring.Web.Atlas")
The example is working but I commented the AutoCompleteExtender because it's not in ASP.NET AJAX 1.0 beta 2.
Bruno
Bruno Baia
01-20-2007, 01:09 AM
Updated for ASP.NET AJAX 1.0 RC1
Bruno
cechiril
01-29-2007, 09:21 PM
Hi Bruno,
The generated js script file (myWebServiceExporter.asmx/jsdebug) has reference to Spring.Proxy.WebServiceProxy_427fe91d0e56403b9a0bd dc9313d17ce
as opposed to myWebServiceExporter. Is that a bug or what is the way to get a handle on it through javascript then?
Thanks
Bruno Baia
01-29-2007, 10:52 PM
Hi,
Yeah, I've seen that too :s
We can't use it through javascript for now...
I was waiting for the final release to make a clean version.
I'll probably create a new ScriptServiceExporter (or maybe not) that will use the id as the type name.
I'll take care of this within a week.
Stay tuned,
Bruno
Bruno Baia
02-06-2007, 12:43 AM
Hi,
I've updated the code to work with the release version.
And fixed the bug to allow a Spring Web service to be invoked from script.
I'll polish the example and make it available as an integration project.
- Bruno
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.