2

I have a Schedulable class that calls out to a third party GPS service. The problem I'm running into is that when I try to do the initial schedule (via anonymous execution) I get the error:

Not Serializable: System.Http

Is there something I can do to get around it? I need to schedule this code to run every 90 seconds so I can have a near real time display of all of our vehicles' GPS positions. For what it's worth, here's the code:

global class vNextGpsScheduledUpdate implements Schedulable {
    private vNextGps gps = new vNextGps('username', 'password');

    global void execute(
        SchedulableContext sc1) {
        if (sc1 == null) {
            return;
        }

        /// Get a list of vehicles in the GPS service
        List<vNextGpsVehiclesLocation> l2 = gps.getVehiclesLocations();

        /// Get a list of vehicles in the database
        List<vNextVehicle__c> l1 = getVehiclesFromDb();

        /// Loop through each vehicle in the database
        for (vNextVehicle__c v1 : l1) {
            /// Loop through each vehicle in the GPS service
            for (vNextGpsVehiclesLocation v2 : l2) {
                if (v1.Name == v2.name) {
                    v1.GPS_Coordinates__Latitude__s = v2.latitude;
                    v1.GPS_Coordinates__Longitude__s = v2.longitude;
                    v1.GPS_Timestamp__c = v2.timestamp;
                    v1.Odometer__c = v2.odometer;

                    /// Break out of the inner loop if the name matches
                    break;
                }
            }
        }

        /// Update all vehicles in the database
        update l1;

        /// Schedule the next run of this job
        DateTime a1 = DateTime.now().addSeconds(90);                        /// nextRun
        String b1 = String.valueOf(a1.minute()),                            /// minute
               b2 = String.valueOf(a1.second()),                            /// second
               b3 = b1 + ' ' + b2 + ' * * * ? * vNextGpsScheduledUpdate',   /// cronValue
               b4 = 'vNextGpsScheduledUpdate ' + a1.format('hh:mm');        /// jobName

        vNextGpsScheduledUpdate c1 = new vNextGpsScheduledUpdate();

        System.schedule(b4, b3, c1);
        System.abortJob(sc1.getTriggerId());
    }

    @testVisible
    private List<vNextVehicle__c> getVehiclesFromDb() {
        return [
            SELECT  Name,
                    GPS_Coordinates__Latitude__s,
                    GPS_Coordinates__Longitude__s,
                    GPS_Timestamp__c,
                    Odometer__c
            FROM    vNextVehicle__c
            WHERE   (Is_Active__c = true)
                    AND (GPS_Active__c = true)
        ];
    }
}

UPDATE

I managed to get it working using @sfdcfox's answer. In my original version, I had an inner class in my vNextGps class that I used to mimic C#'s WebClient class since I was porting the code from pre-existing C# code. Because of that the scheduler couldn't serialize the class. I have since removed the inner class and merged the functionality directly into the method that needed it.

Sadly, that was still not enough because the scheduler then complained about calling out. To resolve that issue I moved the call out code into a separate Queueable class. In turn the Scheduleable class now just adds it to the queue, and schedules itself again. So far, the Queueable class is being processed almost immediately thereafter, so the end result is what I needed. Oh, I did have to annotate the Queueable methods with @future (Callout = true) for everything to finally work.

Way too much hoop jumping to get it to work, but at least it works now.

  • 1
    What's going on inside class vNextGps - does it do HTTP? – cropredy May 8 '15 at 23:57
  • 1
    Yes, it's calling out to a REST service and receiving JSON responses. – Gup3rSuR4c May 9 '15 at 0:05
  • So, when you instantiate vNextGpsScheduledUpdate , the private variable gps is instantiated and presumably does the HTTP authentication to the remote service. But the scheduled class hasn't yet executed as that won't happen until the CRON scheduled time occurs. This seems out of sequence to me – cropredy May 9 '15 at 0:14
  • Close. When the gps variable is instanced it does not make any requests at that time. API on the other side is stupid and the authentication is pointless (always gives the same token, so I just hard coded it in). The only time a call out is done is when gps.getVehiclesLocations() is called, which is during the execute method's lifetime. – Gup3rSuR4c May 9 '15 at 0:30
  • in the debug logs -- does the failure occur on the anonymous execute or when the execute() starts? There will be two log files if you get to the execute() – cropredy May 9 '15 at 0:36
4

Your vNextGps class has a Http variable as an instance variable. This is causing the vNextGps to fail to serialize. Move the variable to within the function that needs it.

| improve this answer | |
  • Well, technically this is the answer. Sadly, I got a notification that scheduled APEX can't perform callouts which ends this attempt. I'll explore Queueable and see how it fares. – Gup3rSuR4c May 9 '15 at 22:06
  • You need to use Database.AllowsCallouts to use callouts. It's not obvious from the documentation, but there you have it. Queueable classes that allow callouts cannot chain, so you might be better off calling a batch from your scheduler. – sfdcfox May 10 '15 at 18:16
  • I ended up using a shortened version of my Schedulable class. The only thing it does is queue another Queueable class and then schedule it self again. The Queueable class seems to run almost at the same time from what I'm seeing, so it seems to serve my needs. Not perfect, but with these limitations I feel like I'm spending more time trying to trick the platform than working with it. – Gup3rSuR4c May 10 '15 at 19:15
1

I see few problem in your code :

  1. You are trying to schedule class inside execute method itself. It should be executed from Anonymous code or some other place. Check answer to Question 50 here.

  2. crone statement doesn't seems right. You can use this website to create crone statement.

  3. You are aborting job just after schedule.

4. and most important (that is reason for your error I guess), If your code is doing any callout it must be used with Queueable interface or @future annotation.

Here is updated code (I didnt tested it, as i am not aware about complete functionality) :

global class vNextGpsScheduledUpdate implements Schedulable { private vNextGps gps = new vNextGps('username', 'password');

global void execute( SchedulableContext sc1) {
    if (sc1 == null) {
        return;
    }

    /// Get a list of vehicles in the GPS service
    List<vNextGpsVehiclesLocation> l2 = gps.getVehiclesLocations();

    /// Get a list of vehicles in the database
    List<vNextVehicle__c> l1 = getVehiclesFromDb();

    /// Loop through each vehicle in the database
    for (vNextVehicle__c v1 : l1) {
        /// Loop through each vehicle in the GPS service
        for (vNextGpsVehiclesLocation v2 : l2) {
            if (v1.Name == v2.name) {
                v1.GPS_Coordinates__Latitude__s = v2.latitude;
                v1.GPS_Coordinates__Longitude__s = v2.longitude;
                v1.GPS_Timestamp__c = v2.timestamp;
                v1.Odometer__c = v2.odometer;

                /// Break out of the inner loop if the name matches
                break;
            }
        }
    }

    /// Update all vehicles in the database
    update l1; 

} 

   @testVisible
private List<vNextVehicle__c> getVehiclesFromDb() {
    return [
        SELECT  Name,
                GPS_Coordinates__Latitude__s,
                GPS_Coordinates__Longitude__s,
                GPS_Timestamp__c,
                Odometer__c
        FROM    vNextVehicle__c
        WHERE   (Is_Active__c = true)
                AND (GPS_Active__c = true)
    ];
}    
}

Execute below code from anonymous window of developer console :

vNextGpsScheduledUpdate c1 = new vNextGpsScheduledUpdate(); 
//You will need to change this statement
String sch = '20 30 8 10 2 ?';         
System.schedule('vNextGpsScheduledUpdate', sch, c1);

You can read this article to get familiar with this concept.

| improve this answer | |
  • 1
    I dont see any test method inside his class :) – Mohith Shrivastava May 8 '15 at 23:55
  • 1
    Jitendra -- re: pt 3 he doesn't have testcode in his class, he is making a method testVisible, this is perfectly fine – cropredy May 8 '15 at 23:55
  • Ohh My Bad.. I over looked :P – Jitendra Zaa May 8 '15 at 23:57
  • I used a modified version of sample code. 1) The class is scheduling its next run on its own. 2) Cron has been verified correct, but I do have re-work a part of it. 3) I am aborting the currently executing job (when it's done) to clear from the list. 4) I'll look into Queueable, but I need confirmed execution every 90 seconds, not when the platform decides it has resources to spare. Sample code: help.salesforce.com/… – Gup3rSuR4c May 9 '15 at 0:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.