I want time delay before function call in apex code. I already created one delay method but it is not working as per expectation. So, is there any way to get this working.
Thanks in advance.
Probably a better way to do this would be to break up your Apex code such that the part you want to execute later is in a separate method. You can then call this method from another method that has an #future annotation, or use the Apex Scheduler to schedule that code for a future time. Either of these methods will cause the code to be executed asynchronously after your original method has completed (the #future method is easier to implement but the scheduler method has the advantage of running at a predictable time).
If you need something like the sleep() function, one way to do it is to make a call to a http service which will sleep a requested amount of time. This is fairly simple to do, and there are existing publicly available services for it, for example the one at http://1.cuzillion.com/bin/resource.cgi.
First you have to Configure a new Remote Site in SalesForce (Security Controls -> Remote Site Settings), name it however you want but make sure the Remote Site URL matches the above URL, and that the "Active" checkbox is checked.
After that, you can define your method in code like so:
public static void sleep(Integer sleepSeconds) {
Long startTS = System.currentTimeMillis();
HttpRequest req = new HttpRequest();
req.setEndpoint('http://1.cuzillion.com/bin/resource.cgi?sleep=' + sleepSeconds);
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);
Long duration = System.currentTimeMillis() - startTS;
System.debug('Duration: ' + duration + 'ms');
}
Running it yields:
sleep(1);
-> 08:46:57.242 (1242588000)|USER_DEBUG|[10]|DEBUG|Duration: 1202ms
You can easily do this in Visualforce. Either use apex:actionPoller and set the timeout property to whatever you want the interval to be. Or use window.setTimeout(function, 1000) in JavaScript. Then from the function in JavaScript you can either use JavaScript Remoting or apex:actionFunction to call back into Apex.
Related
I checked the docs and stackoverflow but didn't find exactly a suiting approach.
E.g. this post seems very close: Dispatch a blocking service in a Reactive REST GET endpoint with Quarkus/Mutiny
However, I don't want so much unneccessary boilerplate code in my service, at best, no service code change at all.
I generally just want to call a service method which uses entity manager and thus is a blocking action, however, want to return a string to the caller immidiately like "query started" or something. I don't need a callback object, it's just a fire and forget approach.
I tried something like this
#NonBlocking
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public Uni<String> triggerQuery() {
return Uni.createFrom()
.item("query started")
.call(() -> service.startLongRunningQuery());
}
But it's not working -> Error message returned to the caller:
You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread.",
I actually expected quarkus takes care to distribute the tasks accordingly, that is, rest call to io thread and blocking entity manager operations to worker thread.
So I must using it wrong.
UPDATE:
Also tried an proposed workaround that I found in https://github.com/quarkusio/quarkus/issues/11535 changing the method body to
return Uni.createFrom()
.item("query started")
.emitOn(Infrastructure.getDefaultWorkerPool())
.invoke(()-> service.startLongRunningQuery());
Now I don't get an error, but service.startLongRunningQuery() is not invoked, thus no logs and no query is actually sent to db.
Same with (How to call long running blocking void returning method with Mutiny reactive programming?):
return Uni.createFrom()
.item(() ->service.startLongRunningQuery())
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool())
Same with (How to run blocking codes on another thread and make http request return immediately):
ExecutorService executor = Executors.newFixedThreadPool(10, r -> new Thread(r, "CUSTOM_THREAD"));
return Uni.createFrom()
.item(() -> service.startLongRunningQuery())
.runSubscriptionOn(executor);
Any idea why service.startLongRunningQuery() is not called at all and how to achieve fire and forget behaviour, assuming rest call handled via IO thread and service call handled by worker thread?
It depends if you want to return immediately (before your startLongRunningQuery operation is effectively executed), or if you want to wait until the operation completes.
If the first case, use something like:
#Inject EventBus bus;
#NonBlocking
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public void triggerQuery() {
bus.send("some-address", "my payload");
}
#Blocking // Will be called on a worker thread
#ConsumeEvent("some-address")
public void executeQuery(String payload) {
service.startLongRunningQuery();
}
In the second case, you need to execute the query on a worker thread.
#POST
#Produces(MediaType.TEXT_PLAIN)
#Path("/query")
public Uni<String> triggerQuery() {
return Uni.createFrom(() -> service.startLongRunningQuery())
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
}
Note that you need RESTEasy Reactive for this to work (and not classic RESTEasy). If you use classic RESTEasy, you would need the quarkus-resteasy-mutiny extension (but I would recommend using RESTEasy Reactive, it will be way more efficient).
Use the EventBus for that https://quarkus.io/guides/reactive-event-bus
Send and forget is the way to go.
The problem I am running into is that I cannot get the API call to our servers to run when someone enters donations with batches. If the donation is entered individually, it works perfectly fine. But when a batch is used, an error is returned that says a future method cannot called from a future or batch method. So I uncommented "#future (callout=true)" above the method, and it creates the record, but the API call is never made. But if I leave it uncommented, we get the error.
Basically this is how the structure of the code is set up:
Apex trigger class triggers when a new donation is added (Pulls data relating to donation)
The trigger calls a class that has a method that will pull more data about the account and contact from the data pulled from the trigger.
That method then called another method (in the same class) that has an API call to our servers that will send the data to our servers for us to process
Here is the code:
Trigger:
trigger NewDonor on Donation (after insert) {
for(Donation d : Trigger.new)
{
if(d.Stage == 'Closed Won' || d.Stage == 'Received'){
//Get contacts account id
string accountId = d.AccountId;
decimal money = d.Amount;
string donation_amount = money.toPlainString();
string donation_id = d.Id;
ProcessDonor.SerializeJson(accountId, donation_amount, donation_id);
}
}
}
Class:
public class ProcessDonor {
String account;
String JSONString;
//Method to make http request
public static String jsonHandler(String json1, String json2, String account, String donoAmount, String donoId){
String JSONString1 = json1;
String JSONString2 = json2;
String accountId = account;
String donation_amount = donoAmount;
String donation_id = donoId;
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://urlToTerver');
request.setMethod('POST');
//Set headers
request.setHeader('Content-Type', 'application/json;charset=UTF-8');
request.setHeader('accountDataFields', JSONString1);
request.setHeader('contactDataFields', JSONString2);
request.setHeader('accountId', accountId);
request.setHeader('donationAmount', donation_amount);
request.setHeader('donationId', donation_id);
// Set the body as a JSON object
HttpResponse response = http.send(request);
// Parse the JSON response
if (response.getStatusCode() != 200) {
System.debug('The status code returned was not expected: ' +
response.getStatusCode() + ' ' + response.getBody());
return 'The status code returned was not expected: ' + response.getStatusCode() + ' ' + response.getStatus();
} else {
System.debug(response.getBody());
return response.getBody();
}
}
//Main method 2
//#future (callout=true)
public static void SerializeJson(String account, String amount, String donationId) {
String accountId = account;
String donationAmount = amount;
String donoId = donationId;
List<Account> accountQuery = Database.query('SELECT Id, Name, FirstGiftDate__c, LatestGiftDate__c, LifetimeDonationCount__c FROM Account WHERE Id = :accountId');
Decimal donationCount = accountQuery[0].LifetimeDonationCount__c;
Date latestGiftDate = accountQuery[0].LatestGiftDate__c;
Date firstGiftDate = accountQuery[0].FirstGiftDate__c;
if(donationCount == 1.00) {
System.debug(accountId);
System.debug(donationAmount);
//Make database query and set result to json
String JSONString1 = JSON.serialize(Database.query('SELECT Id, Contact__c, Name, Billing_Street, Billing_City, Billing_State, Billing_Postal_Code, EnvelopeSalutation__c, FirstGiftDate__c FROM Account WHERE Id = :accountId limit 1'));
String JSONString2 = JSON.serialize(Database.query('SELECT AccountId, Addressee__c, salutation FROM Contact WHERE AccountId = :accountId limit 1'));
String response = jsonHandler(JSONString1, JSONString2, accountId, donationAmount, donoId);
}
}
}
Maybe I just don't understand how batches fully work, but I am not sure how else to make this work.
Any help on this would be greatly appreciated!
The issue is that you are looping in the trigger and trying to make multiple future calls. Instead, move the loop to the future call. Loop through the records and make the callouts there. But be aware, there is a limitation on how many callous you can make. If the calls to your server don't have to happen in realtime, You might want to consider writing a batch class that would process all the donations at night.
Are you using Non-Profit Starter Pack (NPSP)? There's no standard object called Donation, NPSP renames Opportunities to Donations... That trigger looks suspicious (if it's Opportunities - it should say StageName, not Stage), you sure it compiled? And the way it's written it'll fire every time somebody edits a closed won opportunity. You probably want to run it only once, when stage changes. Or when some field "sent ok" is not set yet (and on successfull callout you'd set that field?)
"a future method cannot called from a future or batch method" - this suggests there's something more at play. For example a batch job (apex class with implements database.batchable in it) that inserts multiple donations -> calls trigger -> this calls #future -> problem. You shouldn't get that error from "normal" operation, even if you'd use Data Loader for bulk load -> trigger...
Or - do you update some field on donation on successful call? But at that point you're in #future, it will enter your trigger again, schedule another #future - boom headshot. It's bit crude but SF protects you from recursion. Did you post complete code? Do you think you have recursion in there?
The reason calls to external servers have to be asynchronous (#future etc) is that in a trigger you have a lock on the database table. SF can't block write access to this table for up to 2 minutes depending on a whim of 3rd party server. It has to be detached from main execution.
You can make 50 calls to #future method in single execution and each can have 100 HTTP callouts (unless they hit 120s combined max timeout), this should be plenty of room to do what you need.
Assuming worst case scenario, that you really have a batch job that inserts donations and this has side effect of making callouts - you won't be able to do it like that. You have few options to detach it.
Option 1: In trigger you can run System.isBatch() || System.isFuture() || System.isQueueable() and run current code only if it's all false. If we're in "normal" mode, not already asynchronous - you should be able to call the #future method and sendyour donation. This would sort situations where user manually creates one or you have "normal" load with Data Loader for example.
If one of these returns true - don't run the code. Instead have some other batch job that runs every hour? every 5 minutes? and "cleans". Picks donations that are closed won/received but don't have some hidden "sent ok" checkbox set and send just them.
Option 2 Similar but using Platform Events. It's bit fancier. Your trigger would raise an event (notification that can be read by other apex code, flows but also 3rd party systems), you'd write a trigger on events and send your callouts from that trigger. It'd be properly detached and it shouldn't matter whether it's called from UI, Data Loader or apex batch job. https://trailhead.salesforce.com/en/content/learn/modules/platform_events_basics/platform_events_subscribe might help
I'm creating a REST client using Feign. I've got my calls working, but I want to add some timeout support, and I'm having a heck of a time figuring out how to do that.
Feign's documentation says "to use Hystrix with Feign, add the Hystrix module to your classpath. Then use the HystrixFeign builder." Ok, so now I've got this:
service = HystrixFeign.builder()
.decoder(new GsonDecoder())
.target(ProjectService.class, URL_TO_BE_MOVED_TO_PROPS);
Now all of my methods are returning HystrixCommands, which I can execute or queue, but I still can't see how to configure them.
The Hystrix wiki (https://github.com/Netflix/Hystrix/wiki/Configuration) says that configuration should be added into the HystrixCommand constructor like this:
public HystrixCommandInstance(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(500)));
this.id = id;
But my commands are being built/return by Feign, so I don't have access to the constructors.
One more thing worth noting is that the Feign-Hystrix readme (https://github.com/Netflix/feign/tree/master/hystrix) says "To use Hystrix with Feign, add the Hystrix module to your classpath. Then, configure Feign to use the HystrixInvocationHandler," but a Google search for HystrixInvocationHandler points me toward a non-Netflix repo. Even if I used that, I don't see how to configure Feign to use it.
Please tell me I'm being dumb and that this is super simple, which will make me feel gladness that I'm past this issue, and shame for not being able to figure it out on my own.
TL;DR: I want to set timeouts on requests made by my Feign client. How do?
Turns out you can set Hystrix properties using an instance of com.netflix.config.ConfigurationManager (from com.netflix.archaius:archaius-core).
Feign uses method names as HystrixCommandKeys, so you can access their properties using those names:
ConfigurationManager.getConfigInstance().setProperty("hystrix.command." + methodName + ".execution.isolation.thread.timeoutInMilliseconds", 1500);
This is assuming you've used HystrixFeign to construct your client, which wraps each call in HystrixCommand objects.
To simplify, I created a loop of my methods so I could apply the timeout service-wide:
private void configureHystrix() {
Method[] methods = ProjectService.class.getMethods();
String methodName;
for(int i = 0; i < methods.length; i++) {
methodName = methods[i].getName();
ConfigurationManager.getConfigInstance().setProperty(String.format("hystrix.command.%s.execution.isolation.thread.timeoutInMilliseconds", methodName), config.getTimeoutInMillis());
}
}
After some debugging I managed to set Hystrix timeout as follows:
HystrixFeign.builder()
.setterFactory(getSetterFactory())
.target(...);
// copy-paste feign.hystrix.SetterFactory.Default, just add andCommandPropertiesDefaults
private SetterFactory getSetterFactory() {
return (target, method) -> {
String groupKey = target.name();
String commandKey = Feign.configKey(target.type(), method);
return HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(15000));
};
}
I want to store data in database in every minute . For the same what should I use Service, AsyncTask or anything else. I go through various link which made me more confused .
I read the developer guide and came to know about getWritableDatabase
Database upgrade may take a long time, you should not call this method from the application main thread,
Then first I think I will use AsyncTask then about this
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
After that I think I can use Service then about Service
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
Here I am not able to understand what should I use to store data in database periodically. Please help me here as struck badly.
Thanks in advance
you cant do a lot work on the UI thread, so making database operations you could choose different approaches, few of them that I prefer to use are listed below;
Create a thread pool and execute each database operation via a thread, this reduces load on UI thread, also it never initializes lot of threads.
You can use services for updating the database operations. since services running on UI thread you cant write your operations in Services, so that you have to create a separate thread inside service method. or you can use Intent service directly since it is not working on UI Thread.
here is developer documentation on thread pool in android
and this is the documentation for IntentService
UPDATE
This will send an intent to your service every minute without using any processor time in your activity in between
Intent myIntent = new Intent(context, MyServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 60); // first time
long frequency= 60 * 1000; // in ms
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), frequency, pendingIntent);
Before that check if you really need a service to be started in each minute. or if you can have one service which checks for the data changes in each minute, starting new service would consume maybe more resources than checking itself.
UPDATE 2
private ping() {
// periodic action here.
scheduleNext();
}
private scheduleNext() {
mHandler.postDelayed(new Runnable() {
public void run() { ping(); }
}, 60000);
}
int onStartCommand(Intent intent, int x, int y) {
mHandler = new android.os.Handler();
ping();
return STICKY;
}
this is a simple example like that you can do
I have an APEX class that is used to send an email out each day at 7PM:
global class ReportBroadcaster implements Schedulable {
global ReportBroadcaster() {
}
global void execute(SchedulableContext sc) {
send();
}
global void send() {
PageReference page = new PageReference('/apex/nameofvfpage');
page.setRedirect(true);
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setSubject('Example Subject');
email.setHtmlBody(page.getContent().toString());
email.setToAddresses(new String[]{'test#test.com'});
Messaging.sendEmail(new Messaging.SingleEmailMessage[]{email});
}
}
When I execute the send() method via an instance of the ReportBroadcaster via anonymous APEX, it is delivered as expected. However, when I schedule the class, the email is delivered with a blank body. If I switch the email body to plain text, it delivers fine (but that doesn't work for me).
How do I make this work?
UPDATE:
You cannot call getContent() on PageReference instances from either scheduled APEX or #future methods (I'm not sure why that would be, but it is what it is). I think that the solution will be to create a web service that I'll call from the #future method. Seems incredibly hacky, but I'm not sure what else I could do.
FINAL UPDATE:
This is how to send HTML emails from scheduled APEX:
Create a class that implements the Schedulable interface.
Have the execute() method call an #future method.
Have the #future method call a web service enabled method in the class that sends the email.
While this approach is roundabout, it works.
getContent() method is not supported in scheduled Apex. See the last line of this page:
http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_scheduler.htm
I do not know of the top of my head why this doesnt work (it should), but I can maybe suggest a workaround.
You can convert your vforce page into vforce Email Template (or create a new based on the old if you are also using the page somewhere else) and then use that template as the source for your email. Key points to check in the documentation are SingleEmailMessage.setTemplateId in apex docs and <messaging:*> components in vforce docs.
I also faced same problem and was able to find Workaround. I have documented my solution here and hope it will help others.
http://www.shivasoft.in/blog/salesforce/apex/send-email-with-generated-pdf-as-attachment-from-trigger/
Regards,
Jitendra Zaa