You need to sign in to do that
Don't have an account?
Scheduled Apex and HTTP Callouts
Is this supported? We're using the Twitter app, and I wrote a schedulable class that calls the AutomationWrapper included in this package to update our conversations every ten minutes. The code in my class executes on its own as expected, but when it's run via the scheduler, it appears that the HTTP callouts are simply skipped.
This is the log. This code takes an absolute minimum of 2 full seconds to execute when working properly. When run from the scheduler, its' done in less than 300ms, which leads me to believe that the HTTP callouts are just being skipped.
18.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;VALIDATION,INFO;WORKFLOW,INFO1:30:0.26|EXECUTION_STARTED1:30:0.26|CODE_UNIT_STARTED|[EXTERNAL]Twitter Synch 41:30:0.41|SOQL_EXECUTE_BEGIN|[4,40]|Aggregations:0|select id from sf4twitter__twitter_account__c where name = 'logmeinhelp' limit 11:30:0.48|SOQL_EXECUTE_END|[4,40]|Rows:1|Duration:71:30:0.49|METHOD_ENTRY|[5,14]|sf4twitter.AutomationWrapper.doSearchAccount(Id, Integer)1:30:0.49|ENTERING_MANAGED_PKG|sf4twitter1:30:0.49|SOQL_EXECUTE_BEGIN|[116,42]|Aggregations:0| Select t.Username__c, t.Unknown_Twitter_Usernames__c, t.Twitter_User_Id__c, t.Twitter_Max_Twitter__c, t.Twitter_Max_Message__c, t.SystemModstamp, t.Password__c, t.OwnerId, t.Name, t.LastModifiedDate, t.LastModifiedById, t.IsDeleted, t.Id, t.Enable_Auto_Case_Creation__c, t.CreatedDate, t.CreatedById, t.Bit_ly_Username__c, t.Bit_ly_Password__c, associate_DM__c , ignore_maxid__c, closed_case_duration__c From Twitter_Account__c t Where Id =:taId 1:30:0.58|SOQL_EXECUTE_END|[116,42]|Rows:1|Duration:91:30:0.59|SOQL_EXECUTE_BEGIN|[27,34]|Aggregations:0|select id, name from Account where name = :Label.TWITTER_ACCOUNT_NAME1:30:0.114|SOQL_EXECUTE_END|[27,34]|Rows:0|Duration:551:30:0.115|SOQL_EXECUTE_BEGIN|[60,21]|Aggregations:0|Select Name, Id, OwnerId, Username__c, Unknown_Twitter_Usernames__c, Password__c, Twitter_Max_Message__c, Enable_Auto_Case_Creation__c, Bit_ly_Username__c, Bit_ly_Password__c, Twitter_Max_Twitter__c, associate_DM__c, associate_cases_same_username__c, closed_case_duration__c From Twitter_Account__c LIMIT 10001:30:0.118|SOQL_EXECUTE_END|[60,21]|Rows:2|Duration:31:30:0.132|SOQL_EXECUTE_BEGIN|[156,17]|Aggregations:0|select id, twitterId__c from Case where twitterId__c in :tids1:30:0.136|SOQL_EXECUTE_END|[156,17]|Rows:0|Duration:41:30:0.139|SOQL_EXECUTE_BEGIN|[399,20]|Aggregations:0|select id, twitter_username__c from Contact where twitter_username__c in :twitterUserNames LIMIT 10001:30:0.142|SOQL_EXECUTE_END|[399,20]|Rows:0|Duration:31:30:0.142|SOQL_EXECUTE_BEGIN|[402,17]|Aggregations:0|select id, twitter_username__c from Lead where IsConverted = false and twitter_username__c in :twitterUserNames LIMIT 10001:30:0.153|SOQL_EXECUTE_END|[402,17]|Rows:0|Duration:111:30:0.153|SOQL_EXECUTE_BEGIN|[361,18]|Aggregations:0|Select Id, twitterID__c, ContactId, Twitter_Username__c from Case where twitterID__c in :twitterIds1:30:0.155|SOQL_EXECUTE_END|[361,18]|Rows:0|Duration:21:30:0.156|SOQL_EXECUTE_BEGIN|[366,40]|Aggregations:0|select Id, Case__c, Case__r.ClosedDate, Case__r.Status, Contact__c, twitterid__c, Parent__c, type__c from Twitter_Conversation__c where twitterid__c in :twitterIds LIMIT 10001:30:0.164|SOQL_EXECUTE_END|[366,40]|Rows:0|Duration:81:30:0.164|SOQL_EXECUTE_BEGIN|[371,40]|Aggregations:0|select Id, Case__c, Case__r.ClosedDate, Case__r.Status, Contact__c, twitterid__c, Parent__c, Content__c, Author_Real_Name__c, Author_Name__c, type__c, In_Reply_To_Status_Id__c, In_Reply_To_User_Id__c from Twitter_Conversation__c where twitterid__c in :twitterReply LIMIT 10001:30:0.168|SOQL_EXECUTE_END|[371,40]|Rows:0|Duration:41:30:0.171|SOQL_EXECUTE_BEGIN|[663,18]|Aggregations:0|select id, twitterId__c, Twitter_Username__c, CreatedDate, ContactId, Contact.Twitter_Username__c from Case where ((Status != :Label.TWITTER_CASE_CLOSED or ClosedDate >= :closeDuration) and Twitter_Username__c IN :fromUsernames) or Id IN :repCasesId order by LastModifiedDate desc LIMIT 10001:30:0.181|SOQL_EXECUTE_END|[663,18]|Rows:0|Duration:101:30:0.181|SOQL_EXECUTE_BEGIN|[677,40]|Aggregations:0|select id, Content__c,Case__c, Parent__c, Author_Name__c from Twitter_Conversation__c where Case__c IN :casesIds LIMIT 10001:30:0.183|SOQL_EXECUTE_END|[677,40]|Rows:0|Duration:21:30:0.183|SOQL_EXECUTE_BEGIN|[705,36]|Aggregations:0|select In_Reply_To_Status_Id__c, id, Case__r.Status, Case__r.twitterId__c, Case__r.Twitter_Username__c, Case__r.CreatedDate, Case__r.ContactId, Author_Name__c from Twitter_Conversation__c where (In_Reply_To_Status_Id__c in :repliedRAStatusTwitterIds) and (Case__r.Status != :Label.TWITTER_CASE_CLOSED or Case__r.ClosedDate >= :closeDuration) order by Case__r.LastModifiedDate desc limit 10001:30:0.187|SOQL_EXECUTE_END|[705,36]|Rows:0|Duration:41:30:0.188|DML_BEGIN|[621,42]|Op:Upsert|Type:Case|Rows:01:30:0.188|DML_END|[621,42]|1:30:0.190|SOQL_EXECUTE_BEGIN|[561,20]|Aggregations:0|select id, twitter_username__c from Contact where twitter_username__c in :twitterUserNames LIMIT 10001:30:0.192|SOQL_EXECUTE_END|[561,20]|Rows:0|Duration:21:30:0.192|DML_BEGIN|[583,42]|Op:Upsert|Type:Case|Rows:01:30:0.193|DML_END|[583,42]|1:30:0.193|SOQL_EXECUTE_BEGIN|[411,42]|Aggregations:0|select TwitterID__c, Twitter_Account__c, Direct_Message__c,type__c from Twitter_Conversation__c where Type__c = :Label.TWITTER_TYPE_REPLIES and Twitter_Account__c in :accounts.keySet() order by TwitterID__c desc limit 11:30:0.201|SOQL_EXECUTE_END|[411,42]|Rows:1|Duration:81:30:0.201|SOQL_EXECUTE_BEGIN|[414,51]|Aggregations:0|select TwitterID__c, Twitter_Account__c, Direct_Message__c,type__c from Twitter_Conversation__c where Type__c = :Label.TWITTER_OUTBOUND_DIRECTMESSAGE and Twitter_Account__c in :accounts.keySet() and Direct_Message__c != null order by TwitterID__c desc limit 11:30:0.205|SOQL_EXECUTE_END|[414,51]|Rows:0|Duration:41:30:0.205|SOQL_EXECUTE_BEGIN|[418,45]|Aggregations:0|select TwitterID__c, Twitter_Account__c, Direct_Message__c,type__c from Twitter_Conversation__c where (Type__c = :Label.TWITTER_TYPE_REGISTERED_ACCOUNT or Type__c = :Label.TWITTER_OUTBOUND_REPLY) and Twitter_Account__c in :accounts.keySet() and Direct_Message__c = null order by TwitterID__c desc limit 11:30:0.211|SOQL_EXECUTE_END|[418,45]|Rows:1|Duration:61:30:0.211|SOQL_EXECUTE_BEGIN|[422,41]|Aggregations:0|select TwitterID__c, Twitter_Account__c, Direct_Message__c,type__c from Twitter_Conversation__c where Type__c = :Label.TWITTER_TYPE_DIRECT_MESSAGE and Twitter_Account__c in :accounts.keySet() order by TwitterID__c desc limit 11:30:0.215|SOQL_EXECUTE_END|[422,41]|Rows:0|Duration:41:30:0.218|DML_BEGIN|[443,3]|Op:Update|Type:sf4twitter__Twitter_Account__c|Rows:11:30:0.238|ENTERING_MANAGED_PKG|sf4twitter1:30:0.238|SOQL_EXECUTE_BEGIN|[5,42]|Aggregations:0|select id, Username__c from Twitter_Account__c where Username__c =: Trigger.new[0].Username__c and ownerId = :UserInfo.getUserId()1:30:0.243|SOQL_EXECUTE_END|[5,42]|Rows:1|Duration:51:30:0.243|CUMULATIVE_LIMIT_USAGE1:30:0.243|CUMULATIVE_LIMIT_USAGE_END1:30:0.256|DML_END|[443,3]|1:30:0.257|METHOD_EXIT|[5,14]|doSearchAccount(Id, Integer)1:30:0.257|METHOD_ENTRY|[6,3]|system.debug(String)1:30:0.258|USER_DEBUG|[6,3]|DEBUG|result: <?xml version="1.0" encoding="UTF-8"?><result><conversations>0</conversations><cases>0</cases><caseComments>0</caseComments><contacts>0</contacts><leads>0</leads><errorCode>null</errorCode><errorMsg>null</errorMsg><pageNumber>0</pageNumber></result>1:30:0.258|METHOD_EXIT|[6,3]|debug(ANY)1:30:0.258|CODE_UNIT_FINISHED1:30:0.258|EXECUTION_FINISHED
The scheduler class that I'm running every 10 minutes
global class SynchTwitter Implements Schedulable{ global void Execute(SchedulableContext SC){ sf4twitter__twitter_account__c acc = [select id from sf4twitter__twitter_account__c where name = 'mytwitteraccountname' limit 1]; string s = sf4twitter.automationwrapper.doSearchAccount(acc.id,0); system.debug('result: ' + s); }}
I let this run for an hour, and no results were ever pulled back. I ran the code inside the execute method on its own via the system log window, and it pulled back the conversations I was expected to be pulled.
The documentation for the Spring '10 release does not indicate that HTTP callouts are not supported in Scheduled Apex. I'm not willing to write a complete class just to confirm whether or not it's supported, and I can't see any issues with the Twitter class debug log (it's a managed package, so I can't see what's being done where).
Please advise.
Hi paul-lmi,
no Web Callouts are not supported inside a Scheduled Apex Run. You can see the ErrorLog inthe Debuglogs it clearly says, that during a scheduled Run an WebCallout can not be done.
If you only do up to 10 Callaouts per Scheduled Run, you can use a Future Method to do the Callout. This however will not run with Batch runs which are making Callouts.
I'm apparently operating under the same delusion. Just coded up the same kind of scenario (callout to a REST service on a scheduled basis) and I'm not seeing anything happen either. :-\
Any work-arounds for this?
Jerry H.
Not being constructive here, but I really need to vent. Why, oh why, would Salesforce develop any app geared at social integration/interaction, that doesn't support real time, and scheduled, actions to occur in a speedy manner? The Twitter integration requires custom indexes to be created on 3 objects in SF if the customer's org is large, yet those aren't documented at all and getting that done is like pulling teeth, but worse. Fine, workaround, schedule the actions instead of forcing the user to do them. Great. Nope. Can't even do that because callouts are just blatantly SKIPPED in the scheduled Apex code.
Does Salesforce actually think all our users are sitting there, in front of the app, hitting refresh? I certainly hope not...
Expecting admins to kick off some kind of scheduled process in a third party scheduler is ridiculous. We use Salesforce because we don't want to deal with maintaining off-platform functionality. We can get past the fact that Salesforce will likely never give us full CRON-like functionality (BTW, scheduled Apex is NOT CRON, marketing, kthnx), if you just take a leap of faith on the fact that in order to schedule Apex we already have to go through a number of hoops that are reducing the potential for your users to take down OUR platform.
</rant>
Hi Paul,
Just wanted to know, Is there any other way to get the http callouts from scheduler.
I am also facing the same problem that my scheduler is also not fetching any data for twitter accounts.
Just a guess, can we use any other scheduler for this, like cronkit, boomi, etc.
Will they work for twitter implementation.
Any help is appreciated.
use an @future method for your callout and it'll work. it's not an ideal solution, but it can be done.
Thanks Paul that worked.
The below code works fine when I am searching for 1st page. Now when I am searching for next page I am getting following error:
Please help how can I search for another page by calling the below method again and again.
sf4twitter.automationwrapper.doSearchKeyword(twitsearch.id,pageNo);
I need to search till end. Please advice.
Below is code which I am running to search world cup tweet results:
global class SynchTwitterSearchWorldCup Implements Schedulable{
global void Execute(SchedulableContext SC){
doSynch();
}
@future (callout=true)
static void doSynch(){
sf4twitter__Twitter_Search__c twitsearch= [SELECT Id, Name, sf4twitter__Twitter_Keyword_Search__c FROM sf4twitter__Twitter_Search__c where name ='WorldCup' limit 1];
system.debug('tsearch : ' + twitsearch.name + ' and ID '+ twitsearch.id + ' and Keyword: '+ twitsearch.sf4twitter__Twitter_Keyword_Search__c);
string s =sf4twitter.automationwrapper.doSearchKeyword(twitsearch.id, 1);
system.debug('result for Search - '+ twitsearch.name +': ' + s );
//Not Working - Doing Nothing but delay so that previous records gets commited
//Integer start = System.Now().millisecond();
//while(System.Now().millisecond()< start+150){
//}
if(s != null && s.indexOf('</pageNumber>', 0) != -1)
{
Integer pageNo = integer.valueof(s.substring(s.indexOf('<pageNumber>', 0) +12, s.indexOf('</pageNumber>', 0))) ;
system.debug('pageNo : ' + pageNo );
while(pageNo >0){
string subPages = sf4twitter.automationwrapper.doSearchKeyword(twitsearch.id,pageNo);
system.debug('result for SubPages for ' + twitsearch.name + 'for Page: ' + pageNo + ': '+ subPages );
pageNo = integer.valueof(subPages.substring(subPages.indexOf('<pageNumber>', 0) +12, subPages.indexOf('</pageNumber>', 0)));
system.debug('pageNo for subpages returned: ' + pageNo );
}
}
}
}
And the log is as follows:
Log
I'm not sure on the pagination. This is my class (it's an account-based one, not search-based, but I presume the underlying Twitter integration code is the same).
From what I understand, the error you're getting is due to try ing to make a callout when you have an SObject that has been updated in memory, but not committed to the DB. I'm not too sure how to approach that issue, since you don't have access to the Twitter class itself to confirm it's inserting the SObject after each page. I will note, that theoretically, this will fail after the 10th page though, since you can only do 10 callouts per execution.
Thanks Paul for the information.