Asynchronous Web Service Method Failure

T

Turbo

Greetings to all

I have a set of web services that create reports upon request. These
reports can take the form of XML Spreadsheets or dynamically generated .pdf
files. I call the create report methods async to give the user the
opportunity to cancel a long running report. However, any create report
method that opens a stream reader (in order to import some XML for a
spreadsheet) or writer (in order to generate the XML spreadsheet file) cannot
be run async. They run fine synchronously, but for some reason, opening an
IO.Stream causes a "there was an error during async processing" error. The
interesting thing is, this error is given in a message box, not a typical
ASP.NET error page. Any thoughts?
 
D

Dan Rogers

Some questions,

When you say async - do you mean you have created a one-way SOAP method?
(this is the right way to think about async for web services). When you
have a one-way method, you don't get to write a response. In other words,
async server side methods have a "void" signature always. If you try
marking a non-void method with the one way soap attribute, that is an error.

That aside, you should consider using SOAP with attachments if you are
returning files (no longe async methods).

I hope this helps

Dan Rogers
Microsoft Corporation
--------------------
 
B

Bruce Johnson

The fact that you're getting the error message in a message box means
that the client side of the process is catching and processing the
exception. You might want to try getting some information closer to the
actual call to see if you see anything useful.

Also, how large are the files that you're moving back and forth. I seem
to recall some default limit of around 4MB for request/response sizes in
ASP.NET.

Bruce Johnson [.NET MVP]
http://www.objectsharp.com/blogs/bruce
 
T

Turbo

Here is the procedure. I call an asynchronous web service method for a
particular report. I don't pass any files, just parameters for the report,
such as start date and end date. The web service methods don't return any
files, just URL's to the files so I can execute a response.redirect when the
web service is done creating the report. Some of my reports are XML
spreadsheets, so to avoid have to create the XML from scratch, I use existing
files and then add the necessary data elements to them. Within the web
service method that is creating the report, I open a streamreader or use the
XMLDocument Load method in order to get the static XML loaded into the
XMLDocument. When I'm done, I use a streamwriter to create an XmlTextWriter
to save the document. These methods work fine when done synchronously, but
fail when done async. Only methods that use objects derived from
streamreaders and writers fail when run async. I hope I'm making myself
clear. The error message box that pops up only occurs during development.
It isn't a client issue.
 
D

Dan Rogers

I'm still confused about what you mean by async. How are you invoking them
a) when it works, and b) when it doesn't work. Since you are returning
values to the caller, I am pretty sure that these are not async server side
operations, but you are referring to the non-blocking (beginMethodXX) on
the caller side as "async".

is this the case? If so, there is no difference in the way the call is
dispatched and handled on the server side - so again, my confusion as to
why the stream reader thinks the thread is async.

Something in the description here (topology - e.g. what code are you trying
to invoke on what side of a web service method call) is eluding me.

Dan
--------------------
Thread-Topic: Asynchronous Web Service Method Failure
thread-index: AcTi3k4zag4FdGNeSzOsg9ucQ1yLug==
X-WBNR-Posting-Host: 65.118.1.233
From: "=?Utf-8?B?VHVyYm8=?=" <[email protected]>
References: <[email protected]>
 
T

Turbo

Let's say I have a web service called wsReportGenerator that has a method
called CreateReport. This method will create a .pdf or XML spreadsheet and
return the URL of the file created.

Class wsReportGenerator
Inherits System.Web.Services.WebService
<WebMethod()> Public Function CreateReport() as String

create report logic is here and the file (either .pdf or XML
spreadsheet) is
is saved to the hard drive somewhere on the server.

return "\ReportFolder\ReportName.pdf" ' virtual location of report

End Function
End Class

So, when I call this method where appropriate, it's

dim ws as new wsReportGenerator()
or using the proxy from a web reference, ProxyReportGenerator()
dim URL as string = wsReportGenerator.CreateReport()
Response.Redirect(URL, True)

Because the CreateReport method can take a while to run, I call it
asynchronously using a proxy so it looks more like

dim ws as new ProxyReportGenerator()
ws.BeginCreateReport(callback, ws)

Obviously I need to check to see if it's finished every now and then so in
my callback procedure I have
dim URL as string = ws.EndCreateReport(iAsyncResult)
I save the URL in a session variable so the next time I check, it's there
and I can redirect.

When I call a method that opens stream readers or writers somewhere within,
I have no problem running them using the first way, which is synchronous.
They fail when I use the Proxy/Begin method, which invokes the method
asynchronously. Methods that don't open streams run either way with no
problem.

I appreciate you taking the time to look at this. I'm having trouble
explaining it. When I say async in this case, I mean using the
Proxy.BeginMethodName where Proxy would be derived from
System.Web.Services.Protocols.SoapHttpClientProtocol, the class generated
from setting a web reference to a service.
 
T

Turbo

In clarification of my previous post, I have found that I cannot call web
service methods through a proxy if the method opens a stream reader or stream
writer. If I call the CreateReport method using the proxy, it will timeout.
If I call the BeginCreateReport method, it fails when attempting the use the
stream. The only way I can get the method to run successfully is to create
an instance of the web service directly, without using a proxy.
 
D

Dan Rogers

Hi,

Where does this fail - on the caller, or on the service implementation?

The tricky part is that the service implementation (on some far away
server) has no clue that it was called via a delegate. So if it is failing
on the server, I'm at a loss to understand why.

You mentioned that you can call it directly or call it via the proxy. If
you are calling it directly, well, then yes, I can see how the server might
know. What is the fault that you are receiving? Can you provide the stack
trace?

The message box part is still confusing to me. This isn't something you
would see on a web service implementation since there is no logged in
winstation to display a dialog box through.

I'm pretty sure that something in the description of the topology you are
seeing is still not clear to me.

There are several things that I can think of that would make IO on a stream
and async run afoul. The first would be two threads operating on the same
stream simultaneously. But I can't see your implementation, so it isn't
clear to me if you are somehow sharing a stream between two threads. My
own imagined implementation of your report writer is that it opens a file,
writes to it, and then closes it. Is there any chance that you're
encountering two requests hitting the same file name at the same time?

Dan

--------------------
Thread-Topic: Asynchronous Web Service Method Failure
thread-index: AcTjz7CGUISXN6yDRvuJPaoyQ6gWjg==
X-WBNR-Posting-Host: 68.74.70.108
From: "=?Utf-8?B?VHVyYm8=?=" <[email protected]>
References: <[email protected]>
<[email protected]>
 
D

Dan Rogers

This could be a security issue. Because the request is originating thru
the web service (asp.net), the permissions granted to the thread are indeed
sandboxed. If you are calling the class dirctly, you are not encountering
the sand box. Have you tried treating this as a permissions thing? For
instance, if you only try to write to the services directory, and not to a
root qualified path, do you have better luck? I should have thought of
this possibility since it is such a common case.

If you try to over-write an existing file in the local service directory,
and you've explicitely granted permissions to the ASPNET_IIS web service
account to that file, does it work any better? WS that write files are
indeed problematic because they are so tightly sand boxed. If you think
about it, the potential under high load to knock your server over when the
disk is full is extremely likely. A common attack scenario involves web
exposed calls that save files.

Dan
--------------------
Thread-Topic: Asynchronous Web Service Method Failure
thread-index: AcTkUwPdMeARkT8bTT+WHDnZWsK+gg==
X-WBNR-Posting-Host: 65.118.1.233
From: "=?Utf-8?B?VHVyYm8=?=" <[email protected]>
References: <[email protected]>
<[email protected]>
 
T

Turbo

You're probably right, and it's something I should have thought of myself.
Here are the steps involved:

When a session begins, I create a folder for that session in a virtual
directory that is set up for that and nothing else. Let's call it the
application file cache. It is not in the c:\inetpub\wwwroot folder. The
resulting folder looks like \application file caches\sessionid\ so each
session has a temporary disk space where the reports are created. This
folder is removed when the session ends.

When the user requests a report, sometimes a stream is opened to load some
xml that I would need for the report. To open a stream, I use
server.mappath(\application root\subfolder\textfilename).

When the report is completed, a file is created in his/her specific session
folder using server.mappath(\application file caches\sessionid\filename) and
the URL of the file is returned by the web service method. If I use a
streamwriter, the method fails. The error is generated when I try to make
the call to EndCreateReport(IAsyncResult) and this is where the message box
comes up. The real rub is that I don't know if the error occurs when opening
the streams, reading from them, or writing to them. The error is not caught
in the Try/Catch block.

Anyway, I'll look at the security thing.
 
D

Dan Rogers

Here's the part that confuses me. You aren't calling the Endxxx method
more than once are you? That call would normally be made by the event
handler that you specify when you call the Beginxxx method. So you
shouldn't call it on your own to check progress, as the thread would be in
the wrong state. Also, you call that from the client side, not the server
side, yes?

The way you describe this topology just seems out of wack with how it works
in my mind. The message box has to be happening on the client side, yes?
On the server side, it's not a background thread any more than any other
IIS thread is (I mean to say, they are all background threads). If you
call the web service from a standard "add-web-reference" proxy and don't
call the beginXX methods on the proxy, does the error occur? Does the file
get written? Again, I'm not sure yet if we're talking about making a call
thru a proxy and using the proxy side "non-blocking call" behavior, or if
you are using server side async implementation (which is another matter
entirely!).

In general, writing a file outside of the current vroot is going to cause
problems. You just don't have the right permissions by default - have you
thought about writing your file to a database as a blob? This would
eliminate the sandbox issues from the equation. In general writing to disk
is bad juju, and the IIS worker thread is given very restricted permissions
as far as directly accessing system resources such as the local disk.


--------------------
Thread-Topic: Asynchronous Web Service Method Failure
thread-index: AcTnd7LM+hDENbb1So2lC3FcKjjToA==
X-WBNR-Posting-Host: 65.118.1.233
From: "=?Utf-8?B?VHVyYm8=?=" <[email protected]>
References: <[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
 
B

Bruce Johnson

Oooo. Session ids and web services.

I have to ask. Have you enabled sessions for your web service. If
memory serves, this is done by setting EnableSession to true in the
WebMethod attribute. And 'false' is the default, as using sessions in a
web service is tricky.

Also, does your client persist the cookies returned by each of the web
methods and pass them along to subsequent ones. This is not the
'default' behavior of the proxy class that WSDL.exe generates, for
example. I strongly suspect that WSDL.exe is not alone in it's default.
But it means that every web method call will appear to be in a different
session.

Finally, the exception you're getting is probably being through by the
method that is manipulating the stream. In async called, any exceptions
through by the 'remote' method are held onto until the End... method is
called.

Bruce Johnson [.NET MVP]
http://www.objectsharp.com/blogs/bruce
 
T

Turbo

I'm calling the Endxxx method just once from the callback procedure. It
fails on both the Proxy.Beginxxx call and on the Proxy.xxx call. The message
box only occurs when I start the app from VS.NET. It is the same message box
that pops up if the proxy can't reconcile the web method parameters. The
message says "Error occured during async processing." If I browse to the
page, like an end user would do, it simply hangs up IE.

I am using the non blocking web method call from the proxy, not starting a
separate thread in the middle of my web method procedure.

Blobs were an option that I considered, and I will revisit that.

I use the "report cache" folder because it allowed me to be very specific
in the security settings without messing around in the c:\inetpub\wwwroot
folder and it allows me to have multiple apps share the report cache folder.
Also, I cannot return a .pdf document object from a web service method
because the proxy cannot serialize it. I'm using the CETE Dynamic PDF
Generator.

Once again, I very much appreciate your help in this matter.
 
T

Turbo

I'm not using a session, but I pass the session id to the web service method
as the first parameter so that I know where to create the report file. I use
the standard Beginxxx for calling the method:

dim ws as new wsReportGenerator
dim cb as new AsnycCallback(AddressOf Me.ReportCompleted)
dim r as IAsnycResult = ws.BeginCreateReport(Session.SessionID, cb, ws)

When the callback function is called, I execute:

private sub ReportCompleted(ByVal IAResult As IAsyncResult)

dim ws as wsReportGenerator = CType(IAResult.AsyncState, wsReportGenerator)
dim s as string = ws.EndCreateReport()

Response.Redirect(s, True)

end sub

Thank you very much for your help.
 
D

Dan Rogers

You very much can return a file, via a SOAP attachment (e.g. DIME). This
is supported by the WSE 2.0 SP1 toolkit.

One more thing (looked at your other post)... I don't think the
response.redirect call will work. Unless, this is an ASP page calling the
web service. What is the page doing while it is waiting for the async call
to complete? The thread must still be alive for this to work, otherwise I
would expect your results to be completely unpredictable. Since it's
waiting anyway, and you could not have completed painting the response back
to the calling browser, why not just make the page call the service
directly?


--------------------
Thread-Topic: Asynchronous Web Service Method Failure
thread-index: AcTnm21caeIQZdU9R5SF8vY2Aslcgg==
X-WBNR-Posting-Host: 65.118.1.233
From: "=?Utf-8?B?VHVyYm8=?=" <[email protected]>
References: <[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
 
T

Turbo

The response.redirect call works fine. I call the createreport method from
within a web form (aspx) and then the web form displays a message that the
report is being generated. A java script submits the form on the page every
so often so it can check a session variable. My actual code is thus:

Private Sub ReportCompleted(ByVal IAResult As IAsyncResult)

If IAResult.IsCompleted() Then
Dim ws As wsReportGenerator = IAResult.AsyncState
Dim s As String = ws.EndPrintReport(IAResult)
' do some error checking with s here
Session("ReportURL") = s
End If

End Sub

This way, the next time the form is submitted by the javascript, it can
check the Session("ReportURL") variable for a value. If there is one, it
redirects to the report file.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top