I am using IHttpRequest to send a request to a service that has to create a message.
At this moment the Progress procedure holds until it receives a request from the service, while it should just continue. (Fire and forget principle)
This is the class I use to make the call to the service:
USING Progress.Lang.*.
USING OpenEdge.Net.HTTP.ClientBuilder.
USING OpenEdge.Net.HTTP.IHttpRequest.
USING OpenEdge.Net.HTTP.IHttpResponse.
USING OpenEdge.Net.HTTP.RequestBuilder.
USING Progress.Json.ObjectModel.JsonObject.
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS TOOLS.externals.zato.SendDesadv:
DEFINE VARIABLE baseURL AS CHARACTER NO-UNDO.
DEFINE VARIABLE servicePath AS CHARACTER NO-UNDO INITIAL send/json".
DEFINE VARIABLE serviceURL AS CHARACTER NO-UNDO.
DEFINE VARIABLE oRequest AS IHttpRequest NO-UNDO.
DEFINE VARIABLE oResponse AS IHttpResponse NO-UNDO.
DEFINE VARIABLE input_json AS JsonObject NO-UNDO.
CONSTRUCTOR PUBLIC SendDesadv ( INPUT wave_nr AS INTEGER ):
input_json = NEW JsonObject().
input_json:Add("wave_nr", wave_nr).
invokeZato(input_json).
END CONSTRUCTOR.
METHOD PUBLIC VOID invokeService( INPUT input_json AS JsonObject ):
setServiceUrl().
SESSION:DEBUG-ALERT = TRUE.
oRequest = RequestBuilder:Put(serviceURL, input_json)
:AcceptJson()
:REQUEST.
oResponse = ClientBuilder:Build():Client:Execute(oRequest).
END METHOD.
METHOD PUBLIC VOID setBaseUrl( ):
IF LENGTH(PDBNAME(2)) = 3
THEN baseURL = "http://foo".
ELSE baseURL = "http://bar".
END METHOD.
METHOD PUBLIC VOID setServiceUrl( ):
setBaseUrl().
serviceUrl = baseUrl + servicePath.
END METHOD.
END CLASS.
I have already tried to just leave oResponse = ClientBuilder:Build():Client:Execute(oRequest). out of it, but then nothing is sent.
How could I use the "fire and forget" principle here?
Thanks in advance.
I know this is quite late -- but I add this for the benefit of future searchers.
If you have an AppServer, you can do an asynchronous run there. (And can even get a response back when the process finishes, if you like.)
If timing is not critical, there are other possibilities -- like adding the request to a queue and have a separate process to monitor that queue. I am toying with the idea of setting up an IPC mechanism to tell the background monitor that it should look NOW, thus minimizing the lag time between queue and dequeue.
As far as I know it's not possible to do asynchronous communication with IHttpRequest. A huge disadvantage!
A possible workaround might be to use a OS utility like curl or wget for the call instead. There are ways of making os calls asynchronous. They will however spawn new procedures (and not just new threads).
In windows you can do like this basic example:
OS-COMMAND VALUE("dir") NO-WAIT.
In Unix/Linux you can do
OS-COMMAND VALUE("ls &").
This will however introduce new problems: how to input/output data to wget/curl, how to handle "stay" procedures, possibly higher system load etc.
Related
I am trying to pass a dataset as a parameter to a class, but the dataset keeps loosing it's values.
The idea is to put a customer number in the dataset, pass the dataset to the server and let the server fill the dataset with all the customer information and then pass it back to the client.
First calling procedure
This is a unittest procedure calling the ServiceInterface in the server.
USING OpenEdge.Core.Assert.
BLOCK-LEVEL ON ERROR UNDO, THROW.
{USS/Common/Invoice/Include/dsInvoice.i}
DEFINE VARIABLE hProc AS HANDLE NO-UNDO.
RUN USS/Server/Invoice/ServiceInterfaces.p PERSISTENT SET hProc.
TEMP-TABLE ttInvoice:TRACKING-CHANGES = TRUE.
ttInvoice.CustomerNr = CustomerNr.
TEMP-TABLE ttInvoice:TRACKING-CHANGES = FALSE.
RUN UpdateCustomer IN hProc(INPUT CustomerNr, INPUT-OUTPUT DATASET dsInvoice BY-VALUE).
Assert:Equals("MIDDELLANDBAAN 1 B", ttInvoice.DeliveryStreet).
DELETE PROCEDURE hProc.
Service interface on the server
At this moment the dataset still contains all the values. These values are passed to a Business Entity where other values should be added.
PROCEDURE UpdateCustomer:
DEFINE INPUT PARAMETER CustomerNr AS INT.
DEFINE INPUT-OUTPUT PARAMETER DATASET-HANDLE phdsInvoice.
USS.Server.Invoice.BusinessEntity.InvoiceEntity:Instance:UpdateCustomer(INPUT CustomerNr, INPUT-OUTPUT DATASET dsInvoice BY-REFERENCE).
RETURN.
END PROCEDURE.
Business entity
The Business Entity is a singleton, containing an UpdateCustomer method.
When the dataset is passed to this method, it is completely empty.
USING Progress.Lang.*.
USING USS.Common.Interfaces.IBusinessEntity.
USING USS.Server.Invoice.DataAccess.InvoiceBE-DA.
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS USS.Server.Invoice.BusinessEntity.InvoiceEntity IMPLEMENTS IBusinessEntity:
{ USS\Common\Invoice\Include\dsInvoice.i &CLassAccess = "private" }
DEFINE PRIVATE VARIABLE InvoiceDA AS InvoiceBE-DA NO-UNDO.
DEFINE PRIVATE VARIABLE hDSEventHandlers AS HANDLE NO-UNDO.
DEFINE PUBLIC STATIC PROPERTY Instance AS USS.Server.Invoice.BusinessEntity.InvoiceEntity
GET.
PRIVATE SET.
CONSTRUCTOR STATIC InvoiceEntity ():
USS.Server.Invoice.BusinessEntity.InvoiceEntity:Instance = NEW USS.Server.Invoice.BusinessEntity.InvoiceEntity().
END CONSTRUCTOR.
CONSTRUCTOR PUBLIC InvoiceEntity ():
SUPER().
InvoiceDA = NEW InvoiceBE-DA().
END CONSTRUCTOR.
METHOD PUBLIC VOID UpdateCustomer(INPUT pCustomerNr AS INT, INPUT-OUTPUT DATASET dsInvoice ):
DEF VAR hUpdateCustomerService AS HANDLE NO-UNDO.
RUN USS/Server/Invoice/Services/UpdateCustomer.p PERSISTENT SET hUpdateCustomerService.
RUN UpdateCustomer IN hUpdateCustomerService (INPUT pCustomerNr, INPUT-OUTPUT DATASET dsInvoice BY-REFERENCE).
RETURN.
END METHOD.
END CLASS.
I have been working on this for a while now and I hope someone can help me figure this one out.
Yes, as Tim mentioned, you use 2 different datasets in internal procedure UpdateCustomer in ServiceInterfaces.p.
You can change the PARAMETER DATASET-HANDLE to:
DEFINE INPUT-OUTPUT PARAMETER DATASET FOR dsSelectionList.
I am setting up a Mock as shown below. It is passed into the constructor of the target. The target has a Decrypt method that is called twice within the lifetime of the target. Each time the Decrypt method is called, it Disposes of the certificate that is "newed" up in the Setup. However, when calling the Decrypt object the second time, I am getting an ObjectDisposed method upon attempting the decryption. If I replace this Mock with a Fake implementation of ICertificateHelperAdapter that calls GetCertificate(), then the second call to Decrypt works properly.
I am deducing that when I use the Mock, it is not returning me a new instance of the object on subsequent calls to GetCertificate. Is this by design?
private Mock<ICertificateHelperAdapter> GetCertificateHelperAdapter()
{
Mock<ICertificateHelperAdapter> certificateHelper = new Mock<ICertificateHelperAdapter>();
certificateHelper.Setup(
ch => ch.GetCertificate(CertStoreName.My, StoreLocation.LocalMachine, It.IsAny<string>())).Returns(this.GetCertificate()).Verifiable();
return certificateHelper;
}
private X509Certificate2 GetCertificate()
{
return new X509Certificate2(Environment.CurrentDirectory + "\\" + "azureconfig.pfx", "dingos");
}
The different overloads of Returns<T> behaves differently:
The one with T Returns<T>(T value) what you are using is always returning the same instance.
But there is a lazy version which uses Func<T>. They are looks like T Returns<T>(Func<T> value) and they will evaluate each time the parameter function when the setup method is called.
Sample from the Moq site:
// lazy evaluating return value
mock.Setup(foo => foo.GetCount()).Returns(() => count);
Change your setup to:
certificateHelper.Setup(ch =>
ch.GetCertificate(CertStoreName.My, StoreLocation.LocalMachine, It.IsAny<string>()))
.Returns(() => this.GetCertificate()).Verifiable(); //note the lambda in Returns
And it will call your GetCertificate() twice.
In my attempt to learn flex remoting I came across this
flexService.getRules.addEventListener(ResultEvent.RESULT, loadRules);
here flexService is a remote java object .. In above function call can any one help me that when ResultEvent.RESULT will occur. On studying about ResultEvent in AS document it states as
The event that indicates an RPC operation has successfully returned a result
So keeping that in mind my guess is ResultEvent will be fired when flexService.getRules method will successfully return a list of object,where flexService is object of remote class FlexService having getRules function which returns list of object, Can any one please tell how exactly it works..
Also can some one plz tell me how eventListener can be added to a list of object
PS: I am using Spring as backend
Here you set result to arraycollection
private function loadRules(event:ResultEvent):void
{
var list:ArrayCollection = new ArrayCollection();
list = event.result as ArrayCollection;
}
I'll be going on assumptions since you apparently aren't keen on showing more code or giving pertinent information.
I'm assuming that 'flexService' is a RemoteObject that has set all required properties (destination, endpoint, etc)
I'm assuming that 'getRules' is an available function on your java remote class which returns the information needed.
I'm assuming that everything is being sent over using AMF.
in that case, it's as simple as doing this:
var token:ASyncToken = flexService.getRules(arg1, arg2);
token.addResponder(new Responder(yourResultFunction, yourFaultFunction));
private function yourResultFunction(data:Object):void
{
// Do something with data here
}
private function yourFaultFunction(fault:Object):void
{
// do something if a fault happens
}
Of course, this is very basic and you should try to implement a better pattern (commands) around it.
I'm developing a Flex application and am having some trouble working with asynchronous calls. This is what I would like to be able do:
[Bindable] var fooTypes : ArrayCollection();
for each (var fooType : FooType in getFooTypes()) {
fooType.fooCount = getFooCountForType(fooType);
itemTypes.addItem(fooType);
}
The issue I'm running into is that both getFooTypes and getFooCountForType are asynchronous calls to a web service. I understand how to populate fooTypes by setting a Responder and using ResultEvent, but how can I call another service using the result? Are there any suggestions/patterns/frameworks for handling this?
If possible, I Strongly recommed re-working your remote services to return all the data you need in one swoop.
But, if you do not feel that is possible or practical for whatever reason, I would recommend doing some type of remote call chaining.
Add all the "remote calls" you want to make in array. Call the first one. In the result handler process the results and then pop the next one and call it.
I'm a bit unclear from your code sample when you are calling the remote call, but I assume it part of the getFooCountForType method. Conceptually I would do something like this. Define the array of calls to make:
public var callsToMake : Array = new Array();
cache the currently in process fooType:
public var fooType : FooType;
Do your loop and store the results:
for each (var fooType : FooType in getFooTypes()) {
callsToMake.push(fooType);
// based on your code sample I'm unclear if adding the fooTypes to itemTypes is best done here or in the result handler
itemTypes.addItem(fooType);
}
Then call the remote handler and save the foo you're processing:
fooType = callsToMake.pop();
getFooCountForType(fooTypeToProcess);
In the result handler do something like this:
// process results, possibly by setting
fooType.fooCount = results.someResult;
and call the remote method again:
fooType = callsToMake.pop();
getFooCountForType(fooTypeToProcess);
I have a page that needs to combine data from four different webrequests into a single list of items. Currently, I'm running these sequentially, appending to a single list, then binding that list to my repeater.
However, I would like to be able to call these four webrequests asynchronously so that they can run simultaneously and save load time. Unfortunately, all the async tutorials and articles I've seen deal with a single request, using the finished handler to continue processing.
How can I perform the four (this might even increase!) simultaneously, keeping in mind that each result has to be fed into a single list?
many thanks!
EDIT: simplified example of what i'm doing:
var itm1 = Serialize(GetItems(url1));
list.AddRange(itm1);
var itm2 = Serialize(GetItems(url2));
list.AddRange(itm2);
string GetItems(string url)
{
var webRequest = WebRequest.Create(url) as HttpWebRequest;
var response = webRequest.GetResponse() as HttpWebResponse;
string retval;
using (var sr = new StreamReader(response.GetResponseStream()))
{ retval = sr.ReadToEnd(); }
return retval;
}
This should be really simple since your final data depends on the result of all the four requests.
What you can do is create 4 async delegates each pointing to the appropriate web method. Do a BeginInvoke on all of them. And then use a WaitHandle to wait for all. There is no need to use call backs, in your case, as you do not want to continue while the web methods are being processed, but rather wait till all web methods finish execution.
Only after all web methods are executed, will the code after the wait statement execute. Here you can combine the 4 results.
Here's a sample code I developed for you:
class Program
{
delegate string DelegateCallWebMethod(string arg1, string arg2);
static void Main(string[] args)
{
// Create a delegate list to point to the 4 web methods
// If the web methods have different signatures you can put them in a common method and call web methods from within
// If that is not possible you can have an List of DelegateCallWebMethod
DelegateCallWebMethod del = new DelegateCallWebMethod(CallWebMethod);
// Create list of IAsyncResults and WaitHandles
List<IAsyncResult> results = new List<IAsyncResult>();
List<WaitHandle> waitHandles = new List<WaitHandle>();
// Call the web methods asynchronously and store the results and waithandles for future use
for (int counter = 0; counter < 4; )
{
IAsyncResult result = del.BeginInvoke("Method ", (++counter).ToString(), null, null);
results.Add(result);
waitHandles.Add(result.AsyncWaitHandle);
}
// Make sure that further processing is halted until all the web methods are executed
WaitHandle.WaitAll(waitHandles.ToArray());
// Get the web response
string webResponse = String.Empty;
foreach (IAsyncResult result in results)
{
DelegateCallWebMethod invokedDel = (result as AsyncResult).AsyncDelegate as DelegateCallWebMethod;
webResponse += invokedDel.EndInvoke(result);
}
}
// Web method or a class method that sends web requests
public static string CallWebMethod(string arg1, string arg2)
{
// Code that calls the web method and returns the result
return arg1 + " " + arg2 + " called\n";
}
}
How about launching each request on their own separate thread and then appending the results to the list?
you can test this following code:
Parallel.Invoke(() =>
{
//TODO run your requests...
});
You need reference Parallel extensions :
http://msdn.microsoft.com/en-us/concurrency/bb896007.aspx
#Josh: Regarding your question about sending 4 (potentially more) asynchronous requests and keeping track of the responses (for example to feed in a list). You can write 4 requests and 4 response handlers, but since you will potentially have more requests, you can write an asynchronous loop. A classic for loop is made of an init, a condition, and an increment. You can break down a classic for loop into a while loop and still be equivalent. Then you make the while loop into a recursive function. Now you can make it asynchronous. I put some sample scripts here at http://asynchronous.me/ . In your case, select the for loop in the options. If you want the requests to be sent in sequence, i.e. one request after the previous response (request1, response1, request2, response2, request3, response3, etc.) then choose Serial communication (i.e. sequential), but the code is a bit more intricate. On the other hand, if you don't care about the order in which the responses are received (random order), then choose Parallel communication (i.e concurrent), the code is more intuitive. In either case, each response will be associated with its corresponding request by an identifier (a simple integer) so you can keep track of them all. The site will give you a sample script. The samples are written in JavaScript but it's applicable to any language. Adapt the script to your language and coding preferences. With that script, your browser will send 4 requests asynchronously, and by the identifier you'll be able to keep track of which request the response corresponds to. Hope this helps. /Thibaud Lopez Schneider