Tag Archives: Apex

Using Queueable interface to save page as PDF attachment

Sometimes, it is needed to save current visual force page content as PDF in Salesforce. This could be quite heavy depending upon how long the visual force page is.Ideally, it should be done with combination of @future(Callout = true) & Queueable interface which I’m describing below:

  1. Create a handler class which should have method to save current vf page into PDF attachment.

    @future(callout=true)
    public static void createPDFAttachment(IdcobjId, string PageName){

    Obj__c obj1; // object under which you’ve to save attachment
    //Using object, create name of attachment using your own logic and pass to queueable interface

    string attachmentName = ‘xyz’;
    try {
    myQueuebaleClass pdfGen= new myQueuebaleClass (objId, pageName, attachmentName);
    pdfGenerator.execute(null);
    } catch (Exception e) {
    //log exceptions
    } finally {
    }}

    ////*****************Queueable Interface Class*********************

    public class myQueuebaleClass implements Queueable {

    public myQueuebaleClass(Id objId, String pageName, String attachmentName, ){        //Set member properties

    this.objId= objId;
    this.pageName = pageName;
    this.attachmentName = attachmentName;
    }
    public void execute(QueueableContext context){

    //Building the page URL that will be used to generate the PDF of the form
    PageReference pdfPage = new PageReference(‘/apex/’ + pageName + ‘?ObjectId’ + ‘=’ + objId);

    //Instantiating a new attachment record
    Attachment attachmentObject = new Attachment();

    if (Test.IsRunningTest()) {
    attachmentObject.Body = Blob.valueOf(‘Test’);
    } else {
    //setting the content of the attachment by using Salesforce content and then setting the content type as PDF so that the attachment record is stored as PDF
    attachmentObject.Body = pdfPage.getContent();
    }
    //flag to make sure the attachment record is accessible by the relevant users
    attachmentObject.IsPrivate = false;
    attachmentObject.ParentId = ApplicationId;

    //Query existing attachments of the object

    objects= [SELECT Id,
    (select Id, Description from Attachments)
    from obj__c
    WHERE Id = :objId For Update];

    List<Attachment> attachmentList = new List<Attachment>();
    for (Attachment att : objects[0].Attachments) {
    attachmentList.add(att);
    }

    //If an attachment record exists, overwrite it with the new data.
    if (attachmentList.Size() == 1) {
    attachmentObject.Id = attachmentList[0].Id;
    }

    // clean up multiple PDFs of the same form
    if (attachmentList.size() > 1) {
    // delete them all and start again
    delete attachmentList;
    }
    upsert attachmentObject;

    }

    }

Creating partner.jar file needed to run BULK API

Faced  lots of issues while generating partner.jar file that is required before using Bulk API in your code.

Mentioning few important steps related to this which makes generation of this file  error free

In Salesforce , if you follow this example :
Set up Application for Bulk API

and run command mentioned  there in Command Prompt :

java -classpath pathToJar\wsc.jar com.sforce.ws.tools.wsdlc pathToWSDL\wsdlFilename .\wsdlGenFiles.jar

You’re most likely to get this error :

Could not find or load Main Class  com.sforce.ws.tools.wsdlc

As I’ve non Java Background, I’d faced considerable difficulty in understanding and dealing with this error :

But key is to remember important things related to this :

  1.  We’ve to run com.sforce.ws.tools.wsdlc to generate Partner.jar file 
  2. To run this, we need wsc Jar file , which we need to download from                            ‘http://mvnrepository.com/artifact/com.force.api/force-wsc&#8217;
  3.  To let system find wsc.jar file, we’ve to set class path in the same command (We’ can set it separately also)  using  -classpath <Path of jar file>  syntax
  4. pathToWSDL\wsdlFilename as  suggest , is path of Partner WSDL( Please store in  Same folder as wsc,jar)
  5. .\wsdlGenFiles.jar  : path where ‘to be generated’  parner,jar should be stored.
  6. Will suggest to download  latest wsc jar file  and  Partner WSDL from salesforce and store both of them in same folder ( for e.g , for myself it was D:\BulkApi )

For me the full command was :

java -classpath D:\BulkApi\force-wsc-36.0.0.jar  com.sforce.ws.tools.wsdlc D:\BulkApi\PartnerwsdlDv2.xml  D:\BulkApi\partner.jar

Note : File Names should be proper with extension. When Copying addresses of location, I missed Extention (.jar) and as a result, compilation was giving same error

Even after you run this command , you’re most likely to get big error , a key statement there would be :

Exception in thread “main” java.lang.NoClassDefFoundError: org/stringtemplate/v4/STGroupDir

This is where Salesforce documentation seems like incomplete. We need one more JAR File called ‘StringTemplate’.

Actual name is : ST-4.0.8 (Version could vary)

This jar file should be included in classpath. So new command for Command prompt would be :

java -classpath D:\BulkApi\force-wsc-36.0.0.jar;D:\BulkApi\ST-4.0.8.jar  com.sforce.ws.tools.wsdlc D:\BulkApi\PartnerwsdlDv2.xml  D:\BulkApi\partner.jar.

This should resolve most of error but I faced one more problem : I was getting one more JAVA Error :

which is FileNotFoundException, system was not find some tools.jar file :

After searching I found, I’ve to run above command from ‘bin’ folder where JDK(not JRE) is located, in command prompt.

In my case , it was this : C:\Program Files\Java\jdk1.8.0_20\bin.

NOTE : if JDK folder is not present , then you’ve to download it from oracle site :

http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html

Once I run above command through proper directory It started working fine and generated partner.wsdl :

 

 

 

 

 

Salesforce : Custom logic for daily running of a Apex job at User Selected days & time

Suppose user wants to send email at ‘multiple days of every week’ at particular ‘time’.
For e.g user wants to send email at Every ‘Monday’, ‘Friday’ and ‘Saturday’ at 10 am.

So apex schedular alone can’be suffice in this case as user is selecting different schedule for different records of some object.
We have to provide new fields on same object(used for sending email) , on which user can ‘set’ schedule days and time like this :

Scheduling Fields

Now an apex schedular can be writtern which run based on information selected by user :

But Schedular needs 2 pieces of information to run :

  1. it needs to find if current record needs to be selected for today’s run .
  2. what should be ‘Next Run date & time, so that when schedular run next time , it can pick up records

For this we can create two new fields on same object in which we’ll populate

  1. Current day and Run time
  2. Next day and Run time

Scheduling Fields2

 

In this post, we’ll be finding how to find Current Run time and Next Run time(both Date & Time fields)through apex trigger on same object.
This trigger would be running on ‘before insert’ and ‘before update’

Scenario could be :
trigger populate these dates, schedular then pick up records , which have to run today  and ‘on or before’ Time selected by user in ‘Time of the day’ field.

After that , Schedular will update another fields like status after run (sending emall) and again trigger could run to populate these fields ‘Current Run time’ and ‘Next Run time’

Logic :
Now logic to find ‘Current day & time’ is simple. We’ve to just find if current record has today’s day(e.g Sunday) as one of the value selected in multiSelect picklist and time is ‘before or equal to’ current time, when trigger is run

But time to find logic for ‘next Run Day and time’ is little tricky.
If today is Monday and job has to next Run on Wednesday , then ‘Next Run Day & time’ will be after ‘TWO’ days.
But if today is ‘Friday’ and job has to run on Wednesday’, then ‘next run Day and time’ will be after 5 days.

In nutshell, we’ve to find relative position of days first based on current day
For e.g if today is Friday, then relative position of Saturday will be ‘1’ , Sunday ‘2’, Monday ‘3’, etc.
For this we’ll write other class , which will be called from trigger every time, it run.
It will be sometime like below, returning map of  all days like Monday tuesday with their relative positions w.r.t to current day.

Public class ClsMapofDaysBasedOnCurrentDay
{

   public Map<String,integer> returnMapofDays() { 
    
    Datetime dt = DateTime.newInstance(Date.today(), Time.newInstance(0, 0, 0, 0));
        String dayOfWeek=dt.format('EEEE');
     System.debug('Day : ' + dayOfWeek);  
   //This will return day in String like 'Monday, 'Tuesday', etc
        
        
        Map<string,integer> mpDaytoNumber = new Map<string,integer>() ; 
        if(dayOfWeek == 'Sunday')
         {
          mpDaytoNumber.put('Monday', 1);
          mpDaytoNumber.put('Tuesday', 2);
          mpDaytoNumber.put('Wednesday', 3);
          mpDaytoNumber.put('Thursday', 4);
          mpDaytoNumber.put('Friday', 5);
          mpDaytoNumber.put('Saturday', 6);
          mpDaytoNumber.put('Sunday', 0);
         }
        else if(dayOfWeek == 'Monday')
         {
          mpDaytoNumber.put('Monday', 0);
          mpDaytoNumber.put('Tuesday', 1);
          mpDaytoNumber.put('Wednesday', 2);
          mpDaytoNumber.put('Thursday', 3);
          mpDaytoNumber.put('Friday', 4);
          mpDaytoNumber.put('Saturday', 5);
          mpDaytoNumber.put('Sunday', 6);
        }
        else if(dayOfWeek == 'Tuesday')
         {
          mpDaytoNumber.put('Monday', 6);
          mpDaytoNumber.put('Tuesday', 0);
          mpDaytoNumber.put('Wednesday', 1);
          mpDaytoNumber.put('Thursday', 2);
          mpDaytoNumber.put('Friday', 3);
          mpDaytoNumber.put('Saturday', 4);
          mpDaytoNumber.put('Sunday', 5);
         
         }
         
          else if(dayOfWeek == 'Wednesday')
         {
          mpDaytoNumber.put('Monday', 5);
          mpDaytoNumber.put('Tuesday', 6);
          mpDaytoNumber.put('Wednesday', 0);
          mpDaytoNumber.put('Thursday', 1);
          mpDaytoNumber.put('Friday', 2);
          mpDaytoNumber.put('Saturday', 3);
          mpDaytoNumber.put('Sunday', 4);
         
         }
          else if(dayOfWeek == 'Thursday')
         {
          mpDaytoNumber.put('Monday',4 );
          mpDaytoNumber.put('Tuesday', 5);
          mpDaytoNumber.put('Wednesday', 6);
          mpDaytoNumber.put('Thursday', 0);
          mpDaytoNumber.put('Friday', 1);
          mpDaytoNumber.put('Saturday', 2);
          mpDaytoNumber.put('Sunday', 3);
         }
          else if(dayOfWeek == 'Friday')
         {
          mpDaytoNumber.put('Monday', 3);
          mpDaytoNumber.put('Tuesday', 4);
          mpDaytoNumber.put('Wednesday', 5);
          mpDaytoNumber.put('Thursday', 6);
          mpDaytoNumber.put('Friday', 0);
          mpDaytoNumber.put('Saturday', 1);
          mpDaytoNumber.put('Sunday', 2);
         }
          else if(dayOfWeek == 'Saturday')
         {
          mpDaytoNumber.put('Monday', 2);
          mpDaytoNumber.put('Tuesday', 3);
          mpDaytoNumber.put('Wednesday', 4);
          mpDaytoNumber.put('Thursday', 5);
          mpDaytoNumber.put('Friday', 6);
          mpDaytoNumber.put('Saturday', 0);
          mpDaytoNumber.put('Sunday', 1);
         }
        return mpDaytoNumber;
 }
}

Now ‘ll write trigger like this :

 


trigger FindNextRunTime on user_data__c (before insert, before update) {

    Datetime dt = DateTime.newInstance(Date.today(), Time.newInstance(0, 0, 0, 0));
   String dayOfWeek=dt.format('EEEE');  
   //Above will return day in String like 'Monday, 'Tuesday', etc
       
   String hour = string.valueOf(system.now().hour());
   system.debug('HOUR:' + hour ); 
 // This will return Current hour in User's time zone 
        
    Map mpDaytoNumber = new map();
    // calling helper class to populate this map based on current day.
    ClsMapofDaysBasedOnCurrentDay clmap = new ClsMapofDaysBasedOnCurrentDay();
   mpDaytoNumber  = clmap.returnMapofDays();
         
   // our first target is to find all such records , 
  //out of those currently getting updated, which have to run today 
   //and before current time
           
       For(user_data__c ud : trigger.new)
        {
          if(ud.DevWorld__Week_Selective_Days__c.contains(dayOfWeek) && 
              Integer.valueof(ud.DevWorld__Time_of_the_day__c) 
<=  integer.valueOf(hour))
          { 
               // first finding current date & time of the Run
  ud.DevWorld__Current_datetime__c= dateTime.newInstance(Date.today(), 
Time.newInstance(Integer.valueof(ud.DevWorld__Time_of_the_day__c), 0 , 0, 0));
           
     // Finding next Run Date & Time
    List CurrentRecSelectedDays =   ud.DevWorld__Week_Selective_Days__c.split(';');
system.debug('CurrentRecSelectedDays =' + CurrentRecSelectedDays );
           
      If(CurrentRecSelectedDays.size() > 0 )
          {
              // out of all days selected in multi picklist, 
                 //we've to just find the next day 
                    //out of all days selected in picklist
                    
    integer IntegerMinValue = mpDaytoNumber.get(CurrentRecSelectedDays[0]);
   // this integer is most important. If today is Friday and 
// value selected are Friday, Monday and Thursday, then it  will 
// Return Number corresponding to Monday, i.e. 3
                    For(string dayw: CurrentRecSelectedDays ) {
                      if ( mpDaytoNumber.get(dayw) < IntegerMinValue )
                          IntegerMinValue  = mpDaytoNumber.get(dayw);
                    }
    ud.DevWorld__Next_Run_Time__c = 
dateTime.newInstance(Date.today() +IntegerMinValue , 
Time.newInstance(Integer.valueof(ud.DevWorld__Time_of_the_day__c)
, 0 , 0, 0));  // Note how IntegerMinValue  is added to today's day
                  }
              }
            else{ 
             //ud.Exception__c= 'Day or Time not present in Range';
             }
        } // end of for loop of trigger.new
}