Apex スケジューラ
特定の時間に実行されるように Apex クラスを呼び出すには、まずクラスに Schedulable インターフェースを実装し、Salesforce ユーザインターフェースの [Apex をスケジュール] ページまたは System.schedule メソッドのいずれかを使用してスケジュールを指定します。
Schedulable インターフェースの実装
一定の間隔で実行されるように Apex クラスのスケジュールを設定するには、最初に Salesforce が提供するインターフェース Schedulable を実装する Apex クラスを記述します。
スケジューラは、システムとして実行されます。ユーザがそのクラスの実行権限を持っているかどうかにかかわらず、すべてのクラスが実行されます。
Salesforce ユーザインターフェースを使用してスケジュール済みの Apex ジョブの実行を監視および停止するには、[設定] から または をクリックします。
1global void execute(SchedulableContext sc){}実装されたメソッドは global または public として宣言する必要があります。
次の例では、mergeNumbers と呼ばれるクラスの Schedulable インターフェースを実装します。
1global class scheduledMerge implements Schedulable {
2 global void execute(SchedulableContext SC) {
3 mergeNumbers M = new mergeNumbers();
4 }
5}次の例では、上記のクラスを実装するための System.Schedule メソッドを使用します。
1scheduledMerge m = new scheduledMerge();
2String sch = '20 30 8 10 2 ?';
3String jobID = system.schedule('Merge Job', sch, m);Apex の一括処理クラスで Schedulable インターフェースを使用することもできます。次の例では、batchable と呼ばれる Apex の一括処理クラスの Schedulable インターフェースを実装します。
1global class scheduledBatchable implements Schedulable {
2 global void execute(SchedulableContext sc) {
3 batchable b = new batchable();
4 database.executebatch(b);
5 }
6}一括処理ジョブをスケジュールする簡単な方法は、System.scheduleBatch メソッドをコールすることです。この際、Schedulable インターフェースを実装する必要はありません。
スケジュール済みジョブを追跡するには、SchedulableContext オブジェクトを使用します。SchedulableContext getTriggerID メソッドは、このスケジュール済みジョブに関連付けられている CronTrigger オブジェクトの ID を文字列として返します。CronTrigger をクエリすると、スケジュール済みジョブの進行状況を追跡できます。
スケジュール済みジョブの実行を停止するには、getTriggerID メソッドによって返された ID と共に System.abortJob メソッドを使用します。
クエリを使用したスケジュール済みジョブの進行状況の追跡
Apex ジョブがスケジュールされた後、次の例に示すように、CronTrigger に対して SOQL クエリを実行して、ジョブの実行回数、ジョブの再実行がスケジュールされている日時など、いくつかの項目を取得することにより、このジョブの詳細情報を取得することができます。
1CronTrigger ct =
2 [SELECT TimesTriggered, NextFireTime
3 FROM CronTrigger WHERE Id = :jobID];この例では、ユーザがジョブの ID を保持する jobID 変数を使用していることが前提となります。System.schedule メソッドは、ジョブ ID を返します。スケジュール可能なクラスの execute メソッド内でこのクエリを実行すると、SchedulableContext 引数の変数に対して getTriggerId をコールすることで現在のジョブの ID を取得できます。この変数名が sc であるとすると、変更後の例は次のようになります。
1CronTrigger ct =
2 [SELECT TimesTriggered, NextFireTime
3 FROM CronTrigger WHERE Id = :sc.getTriggerId()];また、CronTrigger レコードに関連付けられている CronJobDetail レコードからジョブの名前と種別を取得することもできます。そのためには、CronTrigger に対してクエリを実行するときに CronJobDetail リレーションを使用します。次の例では、CronJobDetail にあるジョブの名前と種別を含む最新の CronTrigger レコードを取得します。
1CronTrigger job =
2 [SELECT Id, CronJobDetail.Id, CronJobDetail.Name, CronJobDetail.JobType
3 FROM CronTrigger ORDER BY CreatedDate DESC LIMIT 1];CronJobDetail を直接クエリして、ジョブの名前と種別を取得することもできます。次の例では、前の例でクエリした CronTrigger レコードに対してジョブの名前と種別を取得します。対応する CronJobDetail レコード ID は、CronTrigger レコードの CronJobDetail.Id 式によって取得されます。
1CronJobDetail ctd =
2 [SELECT Id, Name, JobType
3 FROM CronJobDetail WHERE Id = :job.CronJobDetail.Id];他の種別のすべてのスケジュール済みジョブを除く、すべての Apex スケジュール済みジョブの合計件数を取得するには、次のクエリを実行します。ジョブ種別には「7」という値が指定されています。これは、Apex スケジュール済みジョブの種別に一致します。
1SELECT COUNT() FROM CronTrigger WHERE CronJobDetail.JobType = '7'Apex スケジューラのテスト
次に、Apex スケジューラを使用したテスト方法の例を示します。
System.schedule メソッドは、匿名プロセスを開始します。つまり、スケジュールされた Apex をテストするとき、結果に対してテストする前にスケジュール済みジョブが終了している必要があります。System.schedule メソッドを実行する前後で、テストメソッド startTest と stopTest を使用して、テストを続行する前にスケジュール済みジョブが終了するようにします。startTest メソッドの後に作成されたすべての非同期コールはシステムによって収集されます。stopTest を実行する場合、すべての非同期プロセスが同期して実行されます。startTest メソッドと stopTest メソッド内に System.schedule メソッドを含めない場合、スケジュール済みジョブは Salesforce API バージョン 25.0 以降で保存された Apex のテストメソッドでは最後に実行されますが、それよりも前のバージョンでは実行されません。
1swfobject.registerObject("clippy.codeblock-9", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17global class TestScheduledApexFromTestMethod implements Schedulable {
18
19// This test runs a scheduled job at midnight Sept. 3rd. 2022
20
21 public static String CRON_EXP = '0 0 0 3 9 ? 2022';
22
23 global void execute(SchedulableContext ctx) {
24 CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime
25 FROM CronTrigger WHERE Id = :ctx.getTriggerId()];
26
27 System.assertEquals(CRON_EXP, ct.CronExpression);
28 System.assertEquals(0, ct.TimesTriggered);
29 System.assertEquals('2022-09-03 00:00:00', String.valueOf(ct.NextFireTime));
30
31 Account a = [SELECT Id, Name FROM Account WHERE Name =
32 'testScheduledApexFromTestMethod'];
33 a.name = 'testScheduledApexFromTestMethodUpdated';
34 update a;
35 }
36}1swfobject.registerObject("clippy.codeblock-10", "9");
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@istest
18class TestClass {
19
20 static testmethod void test() {
21 Test.startTest();
22
23 Account a = new Account();
24 a.Name = 'testScheduledApexFromTestMethod';
25 insert a;
26
27 // Schedule the test job
28
29 String jobId = System.schedule('testBasicScheduledApex',
30 TestScheduledApexFromTestMethod.CRON_EXP,
31 new TestScheduledApexFromTestMethod());
32
33 // Get the information from the CronTrigger API object
34 CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered,
35 NextFireTime
36 FROM CronTrigger WHERE id = :jobId];
37
38 // Verify the expressions are the same
39 System.assertEquals(TestScheduledApexFromTestMethod.CRON_EXP,
40 ct.CronExpression);
41
42 // Verify the job has not run
43 System.assertEquals(0, ct.TimesTriggered);
44
45 // Verify the next time the job will run
46 System.assertEquals('2022-09-03 00:00:00',
47 String.valueOf(ct.NextFireTime));
48 System.assertNotEquals('testScheduledApexFromTestMethodUpdated',
49 [SELECT id, name FROM account WHERE id = :a.id].name);
50
51 Test.stopTest();
52
53 System.assertEquals('testScheduledApexFromTestMethodUpdated',
54 [SELECT Id, Name FROM Account WHERE Id = :a.Id].Name);
55
56 }
57}System.Schedule メソッドの使用
1Seconds Minutes Hours Day_of_month Month Day_of_week optional_year式の値は次のとおりです。
| 名前 | 値 | 特殊文字 |
|---|---|---|
| Seconds | 0 ~ 59 | なし |
| Minutes | 0 ~ 59 | なし |
| Hours | 0 ~ 23 | , - * / |
| Day_of_month | 1 ~ 31 | , - * ? / L W |
| Month | 1 ~ 12、または次のとおりです。
|
, - * / |
| Day_of_week | 1 ~ 7、または次のとおりです。
|
, - * ? / L # |
| optional_year | Null または 1970 ~ 2099 | , - * / |
| 特殊文字 | 説明 |
|---|---|
| , | 値を区切ります。たとえば、複数の月を指定する場合は JAN, MAR, APR を使用します。 |
| - | 範囲を指定します。たとえば、複数の月を指定する場合は JAN-MAR を使用します。 |
| * | すべての値を指定します。たとえば、Month を * と指定すると、ジョブは毎月にスケジュールされます。 |
| ? | 特定の値を指定しません。Day_of_month と Day_of_week のみで使用でき、通常は、特定の値以外を指定しない場合に使用します。 |
| / | 増分を指定します。スラッシュの前の数値は期間の開始を指定し、スラッシュの後の数値は期間の長さを指定します。たとえば、Day_of_month に 1/5 と指定した場合、Apex クラスは月の 1 日から始まり、5 日おきに実行されます。 |
| L | 範囲の終了を指定します。Day_of_month と Day_of_week でのみ使用できます。日で使用すると、1 月の場合は 1 月 31 日、うるう年の 2 月の場合は 2 月 28 日など、L は常に月末日を意味します。Day_of_week のみで使用すると、7 または SAT を意味します。Day_of_week の値と一緒に使用すると、その月で指定した曜日の最後を意味します。たとえば、2L と指定すると、月の最終月曜日が指定されます。L と一緒に値の範囲は使用しないでください。予期しない結果が生じる場合があります。 |
| W | 特定の日に最も近い平日 (月曜日~金曜日) を指定します。Day_of_month でのみ使用できます。たとえば、20W と指定し、20 日が土曜日の場合、クラスは 19 日に実行されます。1W と指定すると、1 日が土曜日の場合、クラスはその前の月ではなく、次の月曜日である 3 日に実行されます。 |
| # | weekday#day_of_month という形式で、月の第 nth 日目を指定します。Day_of_week でのみ使用できます。# の前の数値は、平日 (SUN-SAT) を指定します。# の後の数値は、月の日付を指定します。たとえば、2#2 と指定すると、クラスは毎月第 2 月曜日に実行されます。 |
| 式 | 説明 |
|---|---|
| 0 0 13 * * ? | クラスは毎日午後 1 時に実行されます。 |
| 0 0 22 ? * 6L | クラスは毎月最終金曜日の午後 10 時に実行されます。 |
| 0 0 10 ? * MON-FRI | クラスは月曜日から金曜日の午前 10 時に実行されます。 |
| 0 0 20 * * ? 2010 | クラスは 2010 年の毎日午後 8 時に実行されます。 |
次の例では、クラス proschedule によって Schedulable インターフェースが実装されます。このクラスは、2 月 13 日の午前 8 時に実行するようにスケジュールされています。
1proschedule p = new proschedule();
2 String sch = '0 0 8 13 2 ?';
3 system.schedule('One Time Pro', sch, p);System.scheduleBatch メソッドを使用した一括処理ジョブの実行
System.scheduleBatch メソッドをコールして、将来の指定された時刻に 1 回実行されるように一括処理ジョブをスケジュールできます。このメソッドは一括処理クラスにのみ使用できます。また、Schedulable インターフェースを実装する必要はありません。これにより、一括処理ジョブが 1 回実行されるように簡単にスケジュール設定できます。System.scheduleBatch メソッドの使用方法の詳細については、「System.scheduleBatch メソッドの使用」を参照してください。
Apex スケジューラの制限
-
一度にスケジュールできる Apex ジョブの数は 100 です。現在の数を確認するには、Salesforce の [スケジュール済みジョブ] ページを表示し、データ型の検索条件を [スケジュール済み Apex] にしてカスタムビューを作成します。また、CronTrigger オブジェクトおよび CronJobDetail オブジェクトをプログラムでクエリすることで、Apex スケジュール済みジョブの数を取得することもできます。
- 24 時間でのスケジュール済み Apex の最大実行数は、250,000 または組織のライセンス数の 200 倍の大きいほうです。これは組織全体の制限で、他のすべての非同期 Apex (Apex 一括処理、キュー可能 Apex、スケジュール済み Apex、および future メソッド) と共有されます。この制限のカウント対象となるライセンスは、Salesforce フルユーザライセンスまたは Force.com アプリケーションサブスクリプションのユーザライセンスです。Chatter Free、Chatter カスタマーユーザ、カスタマーポータルユーザ、およびパートナーポータルユーザライセンスは含まれません。
Apex スケジューラの注意点とベストプラクティス
- Salesforce は、指定された時間に実行されるようにクラスをスケジュール設定します。実際の実行は、サービスの使用可能状態に応じて遅れる場合があります。
- クラスをトリガからスケジュールする場合は、細心の注意を払ってください。制限を超えるスケジュールクラスをトリガで追加しないようにする必要があります。特に、API の一括更新、インポートウィザード、ユーザインターフェースを使用したレコードの一括変更、および複数のレコードを一度に更新するすべての処理については十分に考慮してください。
- execute メソッドで追加処理を行うことはできますが、すべての処理が個別のクラスで行われるようにすることをお勧めします。
- スケジュール済みの Apex で getContent および getContentAsPDF PageReference メソッドは使用できません。
- 同期 Web サービスコールアウトは、スケジュールされた Apex からは実行できません。コールアウトを実行するには、@future(callout=true) のアノテーションを付加したメソッドにコールアウトを配置し、このメソッドをスケジュールされた Apex からコールすることで非同期コールアウトを実行します。ただし、スケジュールされた Apex で一括処理ジョブを実行する場合、一括処理クラスからコールアウトを実行できます。「Apex の一括処理の使用」を参照してください。
- Salesforce のサービスメンテナンスによるダウンタイム中に実行がスケジュールされている Apex ジョブは、サービスが再開され、システムリソースが使用可能になったときに実行されるようにスケジュールされます。ダウンタイムの発生時にスケジュール済みの Apex ジョブが実行されていた場合は、ジョブがロールバックされ、サービス再開後に再度スケジュールされます。サービスのメジャーアップグレードの後は、システムの使用率が急増するため、スケジュール済みの Apex ジョブの開始が通常より遅れる可能性があります。