How do I do Asynchronous HTTP requests using CFHTTP tag?
I am looping over a Query result and send some data to a URL via HTTP post.
There are lots of records in the query so cfhttp takes lots of time.
Is it possible to send asynchronous HTTP request in ColdFusion?
Somebody suggested to me that I can create a thread and call cfhttp inside that.
Is there any other way than cfthread?
As #peter-boughton suggested, use threads.
In CF 9.0.1 there is a bug with http inside a thread.
http://forums.adobe.com/thread/978907
http://bit.ly/11RN3vo
So, here's a quick prototype I did:
// cf_sucks_workaround.cfc
component extends="com.adobe.coldfusion.base" {
public function cacheScriptObjects() {
local.tags = ["CFFTP", "CFHTTP", "CFMAIL", "CFPDF", "CFQUERY",
"CFPOP", "CFIMAP", "CFFEED", "CFLDAP"];
for(local.tag in local.tags) {
getSupportedTagAttributes(local.tag);
}
}
}
// async_http.cfm
<cfscript>
lg('start');
main();
lg('done');
void function main() {
CreateObject('component', 'cf_sucks_workaround').cacheScriptObjects();
startThreads();
}
void function lg(required string txt) {
WriteLog(file = 'threads', text = "t#threadNum()# #arguments.txt#");
}
void function sendRequest() {
var httpService = new http(timeout = "3", url = "https://www.google.com");
lg('send http req.');
var httpResult = httpService.send().getPrefix();
lg(httpResult.StatusCode);
}
void function startThreads() {
for (local.i = 1; i LTE 3; i++) {
thread action="run" name="thread_#i#" appname="derp" i="#i#" {
lg("start");
sendRequest();
lg("end");
}
}
}
numeric function threadNum() {
return (IsDefined('attributes') AND StructKeyExists(attributes, 'i')) ? attributes.i : 0;
}
</cfscript>
Produces log output:
t0 start
t0 done
t1 start
t2 start
t3 start
t3 send http req.
t1 send http req.
t2 send http req.
t3 200 OK
t3 end
t1 200 OK
t1 end
t2 200 OK
t2 end
Related
I cannot really find an example on how to use this.
Right now, I'm doing like this:
// Request 10 connections.
ListConnectionsResponse response = peopleService.people().connections()
.list("people/me")
.setRequestSyncToken(true)
.setPageSize(10)
.setPersonFields("names,emailAddresses")
.execute();
I make some changes to my contacts (adding, removing, updating), then I do this:
// Request 10 connections.
ListConnectionsResponse response2 = peopleService.people().connections()
.list("people/me")
.setSyncToken(response.getNextSyncToken())
.setPageSize(10)
.setPersonFields("names,emailAddresses")
.execute();
But it seems like I cannot get the changes I've done earlier, not even if I do them directly from the UI. I'm pretty sure I'm using the sync token in the wrong way.
Update (19/02/2020): In this example I call the API requesting the sync token in the first request (I successfully get the contacts), pause the execution (by breakpoint), delete a contact and update another one (from the web page), resume the execution and then I call the API again with the sync token that I extracted from the previous call. The result is that no change was made for some reason:
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
PeopleService peopleService = new PeopleService.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
// Request 10 connections.
ListConnectionsResponse response = peopleService.people().connections()
.list("people/me")
.setPageSize(10)
.setPersonFields("names,emailAddresses")
.setRequestSyncToken(true)
.execute();
// Print display name of connections if available.
List<Person> connections = response.getConnections();
if (connections != null && connections.size() > 0) {
for (Person person : connections) {
List<Name> names = person.getNames();
if (names != null && names.size() > 0) {
System.out.println("Name: " + person.getNames().get(0)
.getDisplayName());
} else {
System.out.println("No names available for connection.");
}
}
} else {
System.out.println("No connections found.");
}
// CORRECT: 2 CONTACTS PRINTED
// CORRECT: THE SYNC TOKEN IS THERE
String syncToken = response.getNextSyncToken();
System.out.println("syncToken = "+syncToken);
// I SETUP A BREAKPOINT BELOW, I DELETE ONE CONTACT AND EDIT ANOTHER AND THEN I RESUME THE EXECUTING
// Request 10 connections.
response = peopleService.people().connections()
.list("people/me")
.setPageSize(10)
.setPersonFields("names,emailAddresses")
.setSyncToken(syncToken)
.execute();
// Print display name of connections if available.
connections = response.getConnections();
if (connections != null && connections.size() > 0) {
for (Person person : connections) {
List<Name> names = person.getNames();
if (names != null && names.size() > 0) {
System.out.println("Name: " + person.getNames().get(0)
.getDisplayName());
} else {
System.out.println("No names available for connection.");
}
}
} else {
System.out.println("No connections found.");
}
// WRONG: I GET "NO CONNECTIONS FOUND"
Something I've found out is that, when requesting or setting a sync token, you must iterate the entirety of the contacts for the nextSyncToken to be populated.
That means that as long as there is a nextPageToken (wink wink setPageSize(10)), the sync token will not be populated.
You could either:
A) Loop over all the contacts using your current
pagination, doing whatever you need to do at every
iteration, and after the last call retrieve the populated
sync token.
B) Iterate over all the contacts in one go, using the max
page size of 2000 and a single personField, retrieve the
token, and then do whatever you need to do. Note that if
you are expecting a user to have more than 2000
contacts, you will still need to call the next pages using
the nextPageToken.
Here is an exemple of a sync loop, adapted from Synchronize Resources Efficiently. Note that I usually use the Python client, so this Java code might not be 100% error free:
private static void run() throws IOException {
Request request = people_service.people().connections()
.list("people/me")
.setPageSize(10)
.setPersonFields("names,emailAddresses");
// Load the sync token stored from the last execution, if any.
// The syncSettingsDataStore is whatever you use for storage.
String syncToken = syncSettingsDataStore.get(SYNC_TOKEN_KEY);
String syncType = null;
// Perform the appropiate sync
if (syncToken == null) {
// Perform a full sync
request.setRequestSyncToken(true);
syncType = "FULL";
} else {
// Try to perform an incremental sync.
request.setSyncToken(syncToken);
syncType = "INCREMENTAL";
}
String pageToken = null;
ListConnectionsResponse response = null;
List<Person> contacts = null;
// Iterate over all the contacts, page by page.
do {
request.setPageToken(pageToken);
try {
response = request.execute();
} catch (GoogleJsonResponseException e) {
if (e.getStatusCode() == 410) {
// A 410 status code, "Gone", indicates that the sync token is
// invalid/expired.
// WARNING: The code is 400 in the Python client. I think the
// Java client uses the correct code, but be on the lookout.
// Clear the sync token.
syncSettingsDataStore.delete(SYNC_TOKEN_KEY);
// And anything else you need before re-syncing.
dataStore.clear();
// Restart
run();
} else {
throw e;
}
}
contacts = response.getItems();
if (contacts.size() == 0) {
System.out.println("No contacts to sync.");
} else if (syncType == "FULL"){
//do full sync for this page.
} else if (syncType == "INCREMENTAL") {
//do incremental sync for this page.
} else {
// What are you doing here???
}
pageToken = response.getNextPageToken();
} while (pageToken != null);
// Store the sync token from the last request for use at the next execution.
syncSettingsDataStore.set(SYNC_TOKEN_KEY, response.getNextSyncToken());
System.out.println("Sync complete.");
}
I have written a webservice using spring webflux and reactive mongodb connectors, but my client side could be non spring based client.
So, how do I write a plain java code to consume flex at client side?
ServerSide code:
#GetMapping(value = "/findAll")
public Flux<Security> findAll() {
Flux<Security> flux = service.findAll();
return flux;
}
Client side code:
public static void sendRequest() {
try {
long start = System.currentTimeMillis();
for (int i = 0; i <= 100; i++) {
long start1 = System.currentTimeMillis();
URL url = new URL("http://localhost:8080/findAll/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/stream+json");
if (conn.getResponseCode() == 200) {
// url = new URL("http://localhost:8182/status/");
String json = "";
try (BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())))) {
json = br.lines().collect(Collectors.joining());
}
conn.disconnect();
System.out.println("size of each Security: " + json.length());
ArrayList<Security> list = getListOfsecurities(json);
System.out.println(list.get(0).getIsin());
}
}
The above client side gives me an empty array.
I don't think it is possible. It's an asynchronous response.At least you have to use Java 5 Futures to invoke asynchronous response.
i have a web service.
it takes 10-15 seconds to upload completely. But some time due to slow internet connection it takes more than 15 second.
I want to open a message box or pop up box which open when the service takes more than 15 seconds, to display message "Slow Internet Connection".
How can i add timer in my code so that a pop up block open(Or alert says Slow Connection) on page load event when web service takes time more than 15 second to load ?
Please help me on this...
public Quiz()
{
InitializeComponent();
DispatchTimer timer = new DispatcherTimer();
int serviceCount = 15;
Loaded += Quiz_Loaded;
}
protected void Quiz_Loaded(object sender, RoutedEventArgs e)
{
SystemTray.ProgressIndicator = new ProgressIndicator();
SystemTray.ProgressIndicator.IsIndeterminate = true;
SystemTray.ProgressIndicator.IsVisible = true;
SystemTray.ProgressIndicator.Text = "Loading Questions...";
pg2.Visibility = Visibility.Visible;
txtloading.Visibility = Visibility.Visible;
if (!timer.IsEnabled)
{
timer.Tick += timer_Tick;
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
}
}
void timer_Tick(object sender, object e)
{
serviceCount--;
if (serviceCount < 15)
{
PostData();
}
else
{
txtloading.Text = "slow connection.....";
}
}
private void PostData()
{
Uri uri = new Uri("my web service url");
string data = "device_id=test123&quiz_type=all";
WebClient wc = new WebClient();
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
wc.UploadStringAsync(uri, data);
wc.UploadStringCompleted += wc_UploadComplete;
}
public void wc_UploadComplete(object sender, UploadStringCompletedEventArgs e)
{
var rootObject = JsonConvert.DeserializeObject<RootObject>(e.Result);
question = rootObject.questions;
DisplayQuestion();
SystemTray.ProgressIndicator.IsVisible = false;
pg2.Visibility = Visibility.Collapsed;
txtloading.Visibility = Visibility.Collapsed;
}
i want to call message block when web service takes time more than 15 second. & also have a retry button which reload the page again to get all data from service..
You could use a DispatcherTimer that is initialized whenever the first call to the web service is initiated. That way, whenever a given timespan elapses, you can trigger a custom action (such as a notification).
Environment: C#, .Net 3.5, Sql Server 2005
I have a method that works in a stand-alone C# console application project. It creates an XMLElement from data in the database and uses a private method to send it to a web service on our local network. When run from VS in this test project, it runs in < 5 seconds.
I copied the class into a CLR project, built it, and installed it in SQL Server (WITH PERMISSION_SET = EXTERNAL_ACCESS). The only difference is the SqlContext.Pipe.Send() calls that I added for debugging.
I am testing it by using an EXECUTE command one stored procedure (in the CLR) from an SSMS query window. It never returns. When I stop execution of the call after a minute, the last thing displayed is "Calling GetResponse() using http://servername:53694/odata.svc/Customers/". Any ideas as to why the GetResponse() call doesn't return when executing within SQL Server?
private static string SendPost(XElement entry, SqlString url, SqlString entityName)
{
// Send the HTTP request
string serviceURL = url.ToString() + entityName.ToString() + "/";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceURL);
request.Method = "POST";
request.Accept = "application/atom+xml,application/xml";
request.ContentType = "application/atom+xml";
request.Timeout = 20000;
request.Proxy = null;
using (var writer = XmlWriter.Create(request.GetRequestStream()))
{
entry.WriteTo(writer);
}
try
{
SqlContext.Pipe.Send("Calling GetResponse() using " + request.RequestUri);
WebResponse response = request.GetResponse();
SqlContext.Pipe.Send("Back from GetResponse()");
/*
string feedData = string.Empty;
Stream stream = response.GetResponseStream();
using (StreamReader streamReader = new StreamReader(stream))
{
feedData = streamReader.ReadToEnd();
}
*/
HttpStatusCode StatusCode = ((HttpWebResponse)response).StatusCode;
response.Close();
if (StatusCode == HttpStatusCode.Created /* 201 */ )
{
return "Created # Location= " + response.Headers["Location"];
}
return "Creation failed; StatusCode=" + StatusCode.ToString();
}
catch (WebException ex)
{
return ex.Message.ToString();
}
finally
{
if (request != null)
request.Abort();
}
}
The problem turned out to be the creation of the request content from the XML. The original:
using (var writer = XmlWriter.Create(request.GetRequestStream()))
{
entry.WriteTo(writer);
}
The working replacement:
using (Stream requestStream = request.GetRequestStream())
{
using (var writer = XmlWriter.Create(requestStream))
{
entry.WriteTo(writer);
}
}
You need to dispose the WebResponse. Otherwise, after a few calls it goes to timeout.
You are asking for trouble doing this in the CLR. And you say you are calling this from a trigger? This belongs in the application tier.
Stuff like this is why when the CLR functionality came out, DBAs were very concerned about how it would be misused.
i have a function which retrieves values from a webservice , then loops through the returned values and for each returned value does another webservice lookup.
Im having a problem however that when i make the second webservice call within the for loop that the function does not wait for the reuslt and just keeps processing and in turn giving me no value
the code looks like this;
private function getResult(e:ResultEvent):void{
var lengthOfResult:int = e.result.length;
var arrayCollResults:ArrayCollection = new ArrayCollection();
var resultArray:Array = new Array(e.result);
for(var i:int = 0 ; i < lengthOfResult; i++){
var currentName:String = e.result[i].toString();
arrayCollResults.addItem(e.result[i] + ws.getMatches(currentName));
}
acu.dataProvider = arrayCollResults;
}
what can i do to insure that the value of ws.getMatches(currentName) actually returns a value before moving to next line?
The documentation here indicates that you do not call the Web service directly, you need to set up an event listener and handle the response on completed delivery.
From the section "Calling web services in ActionScript":
<?xml version="1.0"?>
<!-- fds\rpc\WebServiceInAS.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.rpc.soap.WebService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
private var ws:WebService;
public function useWebService(intArg:int, strArg:String):void {
ws = new WebService();
ws.destination = "echoArgService";
ws.echoArgs.addEventListener("result", echoResultHandler);
ws.addEventListener("fault", faultHandler);
ws.loadWSDL();
ws.echoArgs(intArg, strArg);
}
public function echoResultHandler(event:ResultEvent):void {
var retStr:String = event.result.echoStr;
var retInt:int = event.result.echoInt;
//Do something.
}
public function faultHandler(event:FaultEvent):void {
//deal with event.fault.faultString, etc
}
]]>
</mx:Script>
</mx:Application>
Put the "arrayCollResults.addItem(...)" segment in the result handler for your ws.getMatches() event.
As far as I know flex/as3 cannot do a blocking wait for the result - you have to add a listener and wait to be notified.