BOS金蝶后臺事務(wù)開發(fā)文檔_第1頁
BOS金蝶后臺事務(wù)開發(fā)文檔_第2頁
BOS金蝶后臺事務(wù)開發(fā)文檔_第3頁
BOS金蝶后臺事務(wù)開發(fā)文檔_第4頁
BOS金蝶后臺事務(wù)開發(fā)文檔_第5頁
已閱讀5頁,還剩30頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、后臺事務(wù)開發(fā)文檔目 錄1.簡單后臺事務(wù)示例32.后臺事務(wù)執(zhí)行過程及原理132.1.后臺事務(wù)定義132.2.后臺事務(wù)發(fā)布142.3.后臺事務(wù)執(zhí)行153.代碼實現(xiàn)后臺事務(wù)定義223.1.非持久化后臺事務(wù)223.2.錯過策略253.3.人工干預(yù)(撤銷,掛起,喚醒)283.4.調(diào)度計劃時間331. 簡單后臺事務(wù)示例在進(jìn)行后臺事務(wù)示例測試之前,我們需要做一些準(zhǔn)備工作,首先,我們要在BOS Studio的BIM視圖下新建幾個元數(shù)據(jù),如圖1所示:如上圖所示,新建了四個元數(shù)據(jù)(其中T_BO_BandOffice.table是根據(jù)實體右鍵導(dǎo)出表直接導(dǎo)出得到的),其實這四個元數(shù)據(jù)很簡單,F(xiàn)ileLogFacade

2、.facade只有一個方法logWriter(String str);該方法實現(xiàn)了向服務(wù)器上的C:/file.log輸入日志。BandOffice.entity上新建了一個方法,該方法和FileLogFacade.facade的方法logWriter(String str)作用一樣,名字為testLog(String str),只是為了示范兩個不同的調(diào)用。實體BandOffice.entity還新建了一個TestLogEvent的事件,該事件可以引用實體本身的方法logWriter,也可以引用功能FileLogFacade的testLog方法。再定義了一個業(yè)務(wù)功能FileLogFunction

3、,在這個業(yè)務(wù)功能中定義了一個操作fileLog;通過應(yīng)用,與實體上的事件關(guān)聯(lián)起來。在定義好這些元數(shù)據(jù)后,我們需要把這些元數(shù)據(jù)進(jìn)行發(fā)布,使服務(wù)器端在運行時能夠加載這些元數(shù)據(jù)。上面是我們對解決方案的發(fā)布方案設(shè)置,如紅框標(biāo)識的,我把元數(shù)據(jù)發(fā)布后生成的代碼放在W:workspacebs_jobdevtest目錄中,如下圖所示,元數(shù)據(jù)發(fā)布后在這個目錄下生成的代碼,這是實體和功能(facade)發(fā)布時生成的代碼,其他元數(shù)據(jù)發(fā)布時不生成代碼。在FileLogFacadeControllerBean中會生成功能定義的_logWriter方法,通過在這個方法中寫邏輯即可實現(xiàn),而實體定義的方法會在BandOffi

4、ceControllerBean中生成_testLog方法。元數(shù)據(jù)發(fā)布后,我們需要重新生成系統(tǒng)子樹,這樣我們在重啟服務(wù)器后可以在事務(wù)任務(wù)選擇樹中看到我們定義的功能。具體生成系統(tǒng)子樹的方法,請參照生成系統(tǒng)子樹的兩種方法.doc。登陸EAS后,我們選擇系統(tǒng)平臺>后臺事務(wù)定義,進(jìn)入后臺事務(wù)定義界面:接著我們就按要求進(jìn)行定義,下面我們定義調(diào)度計劃設(shè)置:定義好后,我們進(jìn)行保存,這個時候我們可以進(jìn)行發(fā)布了,如下圖:我們再進(jìn)入:系統(tǒng)平臺>后臺事務(wù)定義表,我們可以看到我們剛才定義的那個事務(wù)了我們設(shè)置好了調(diào)度計劃,當(dāng)時間到達(dá)時,觸發(fā)該事務(wù),就會執(zhí)行一個事務(wù)實例,我們可以查看該事務(wù)實例,我們也可以直接

5、點擊工具欄中的啟動,這樣會立刻啟動一個事務(wù)實例。從上面可以看出,事務(wù)執(zhí)行成功。我們再來看看我們實現(xiàn)的功能,就是在服務(wù)器目錄下寫入一條時間信息記錄:2. 后臺事務(wù)執(zhí)行過程及原理首先我們要弄清楚,在EAS系統(tǒng)的后臺事務(wù)管理平臺中定義一個后臺事務(wù)的過程是怎樣的,你可以通過對代碼的跟蹤進(jìn)行了解,2.1. 后臺事務(wù)定義首先我們在后臺事務(wù)定義界面上新建一個后臺事務(wù)(如下圖所示),這些后臺事務(wù)定義信息會被保存在T_WFD_PROCESSDEF中,可以在這個界面對已經(jīng)定義好的后臺事務(wù)定義進(jìn)行修改和刪除,當(dāng)我們選擇左邊系統(tǒng)子樹的某個后臺事務(wù)定義節(jié)點,它對應(yīng)的信息就會從該表中獲得并顯示在右邊的界面上?,F(xiàn)在我想描述

6、一下右邊這顆系統(tǒng)子樹的生成過程。這可系統(tǒng)子樹是在該界面的構(gòu)造函數(shù)的initializeUI()方法中進(jìn)行初始化的final BusinessTreeUtil tempPTU = new BusinessTreeUtil(BusinessTreeUtil.PROCESS);(KingdeeTreeModel) this.packagekDTree.getModel().setRoot(null);JobUtils.resetStartTimeMillis();tempPTU.initialBusinessTreeView(this.packagekDTree,true);JobUtils.pri

7、ntTimeMillis("initialBusinessTreeView");this.packagekDTree.addTreeSelectionListener(new InnerTreeSelectionListener();BusinessTreeUtil中有三個靜態(tài)變量,分別為PROCESS,F(xiàn)UNCTION,ENTITY,分別用來生成不同的系統(tǒng)子樹,在對系統(tǒng)子樹進(jìn)行初始化調(diào)用了兩個方法,第一個是initialPackageTree(tree ,flag);這個方法是獲得標(biāo)準(zhǔn)包結(jié)構(gòu)的樹,第二個是initalFunctionOrJobProcessTree(tre

8、e,flag);這個是獲得客戶化的樹形結(jié)構(gòu),通過該方法可以在標(biāo)準(zhǔn)化得包結(jié)構(gòu)下添加功能節(jié)點或后臺事務(wù)定義節(jié)點。如果是生成功能(function)子樹或?qū)嶓w(entity)子樹,這就得獲取這些元數(shù)據(jù)的集合,而它們對應(yīng)的集合類:FunctionObjectCollection和EntityObjectCollection,獲取這些集合的方法為:IMetaDataLoader loader = MetaDataLoaderFactory. getRemoteMetaDataLoader()FunctionObjectCollection funcol = loader.getFunctions()En

9、tityObjectCollection entcol = loader.getEntityCollection();2.2. 后臺事務(wù)發(fā)布后臺事務(wù)定義完成以后,通過發(fā)布,我們就可以在后臺事務(wù)定義報表看到我們定義的后臺事務(wù)了,已發(fā)布的后臺事務(wù)不允許進(jìn)行刪除。public void release(ProcessDef process) throws BOSException JobDef def=JobServiceUtil.toJobDef(ctx, process); Trigger trigger=JobServiceUtil.toTrigger(def, process); IJobS

10、ervice svc=JobServiceFactory.getLocalInstance(ctx); svc.createJobDef(def, trigger, true);后臺事務(wù)定義被發(fā)布以后,產(chǎn)生了一個新的對象JobDef,對用在數(shù)據(jù)庫中的表是T_JOB_DEF。后臺事務(wù)實例(Job)就是根據(jù)這個定義來產(chǎn)生的。由上面的代碼可知,后臺事務(wù)定義(JobDef)和觸發(fā)器(Trigger)是由流程定義(ProcessDef)轉(zhuǎn)換過來的,這是因為在以前的后臺事務(wù)是按照工作流的方式進(jìn)行處理的,每一個后臺事務(wù)定義都會生成一個流程定義保存在數(shù)據(jù)庫表(T_WFD_PROCESSDEF)的一個字段中,我

11、們用代碼實現(xiàn)后臺事務(wù)定義就不必通過這樣獲取流程定義(JobDef)。有上面代碼可知,通過調(diào)用JobService的createJobDef(JobDef def, Trigger trigger, boolean enable);方法創(chuàng)建后臺事務(wù)定義(實際上是通過反射調(diào)用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法),通過該方法將JobDef,Trigger對象保存到數(shù)據(jù)庫中的T_JOB_DEF和T_TRIGGER表中。從而完成了后臺事務(wù)定義的發(fā)布功能。2.3. 后臺事務(wù)執(zhí)行后臺事務(wù)每執(zhí)行一次就會

12、產(chǎn)出一個后臺事務(wù)實例(Job),持久化后臺事務(wù)會將后臺事務(wù)實例信息保存在T_JOB_INST表中。 我們可以通過兩種方式來觸發(fā)后臺事務(wù)執(zhí)行,第一種是通過后臺事務(wù)界面上的“啟動”按鈕直接觸發(fā)產(chǎn)生一個后臺事務(wù)實例,第二種是通過定義一個觸發(fā)器(Trigger)來與該后臺事務(wù)定義關(guān)聯(lián)起來,用觸發(fā)器來觸發(fā)產(chǎn)生一個后臺事務(wù)實例。下面我們分別來討論通過這兩種方法是怎樣觸發(fā)產(chǎn)生后臺事務(wù)實例的。“啟動”按鈕觸發(fā)后臺事務(wù)。當(dāng)我們點擊“啟動”按鈕會觸發(fā)該按鈕的事件,會調(diào)用到該界面JobProcessDefineListUI的actionStart_actionPerformed方法,通過方法調(diào)用,我們可以知道它調(diào)用

13、了JobServiceUIFacade的createJobInstance(jobDefId, null, null),在這個類中,通過獲取到了IjobService接口,調(diào)用了jobservice的createJobInstance方法;public String createJobInstance(String jobDefId, Object param, Timestamp scheduledTime) throws BOSException JobDef def=JobDefCache.getJobDef(ctx.getAIS(), jobDefId); if(def=null)th

14、row new BOSException("job def doesn't exist, its id is "+jobDefId+", datacenter: "+ctx.getAIS();if(scheduledTime=null)scheduledTime=new Timestamp(System.currentTimeMillis()+1000); Job job=def.newInstance(ctx, param, scheduledTime); manager.add(job); return job.getTitle();從上面這

15、個方法可以看出,首先由jobDefId獲取到JobDef對象def,在設(shè)置這個產(chǎn)生的任務(wù)實例的計劃執(zhí)行時間為當(dāng)前時間的下一秒。然后由后臺事務(wù)定義對象def的newInstance(ctx, param, scheduledTime)產(chǎn)生一個后臺事務(wù)實例對象job,再由后臺事務(wù)管理器JobManager(單子模式)調(diào)用add(job)方法把這個任務(wù)實例加載到等待隊列或者就需隊列中去;觸發(fā)器觸發(fā)后臺事務(wù)。如果我們在后臺事務(wù)定義的時候?qū)ζ潢P(guān)聯(lián)了相應(yīng)的觸發(fā)器,則當(dāng)下一次觸發(fā)時間到達(dá)時,觸發(fā)器就會觸發(fā)與之關(guān)聯(lián)的后臺事務(wù)定義(JobDef)產(chǎn)生一個后臺事務(wù)實例(Job)。觸發(fā)器觸發(fā)后臺事務(wù)的過程是怎樣的呢

16、?首先:服務(wù)器啟動時會初始化觸發(fā)器加載類(TriggerLoader),該類實現(xiàn)了IJobHandler, IcoreJobHandler兩個接口,本身就是一個任務(wù)處理器,這個類的作用是:觸發(fā)器加載者,定期從數(shù)據(jù)庫加載新增的觸發(fā)器。其load()方法會定期被調(diào)用執(zhí)行/*加載觸發(fā)器*/private int load()throws ExceptionStringBuffer sql=new StringBuffer();sql.append("select * from T_JOB_TRIGGER where fisvalid='Y' AND fisAutoLoad=

17、'Y' AND "); sql.append("(").append(TriggerIsolationLevel.sqlWhere(dc) .append(")");/符合隔離邊界sql.append(" and fholderid is null");/無持有者/只加載已生效的觸發(fā)器sql.append(" and fvalidateTime<=?");int types=new intTypes.TIMESTAMP;Timestamp now=new Timestamp(Syst

18、em.currentTimeMillis();Object values=new Objectnow;ArrayList items=SQL.executeQuery(dc, sql.toString(), types, values);Trigger tr;for(int i=0; i<items.size();i+)tr=null;trytr=Trigger.from(dc, (HashMap)items.get(i);PulseSource ps=tr.getPulseSource();if(ps=null)throw new BOSException("couldn&#

19、39;t find pulse source for this trigger!");ps.addTrigger(tr);catch(Throwable t) 。(省略) 。(省略)/觸發(fā)器加載間隔為任務(wù)加載間隔的5倍return Configuration.persistantJobCheckInterval()*5;這個方法實現(xiàn)了從數(shù)據(jù)庫表T_JOB_TRIGGER中加載新添加的觸發(fā)器,并返回觸發(fā)器加載時間間隔(上面設(shè)置的是為持久化任務(wù)加載間隔的5倍)protected synchronized void addTrigger(Trigger tr) throws Excepti

20、onTimerPulse pulse=new TimerPulse(tr);if(pulse.nextFireTime()=null)tr.inValid();/禁用TRIGGERthrow new java.lang.IllegalArgumentException("trigger "+tr.getKey()+" has no fire time!");if(pulse.setLoaded()add(pulse);TimerPulseSource的protected synchronized void addTrigger(Trigger tr)方法

21、是通過一個觸發(fā)器新建一個時間觸發(fā)發(fā)生器(TimerPulse),如果獲取不到這個TimerPulse下一次觸發(fā)時間,就禁用這個觸發(fā)器(Trigger),否則設(shè)置這個TimerPulse對應(yīng)的觸發(fā)器(Trigger)的狀態(tài)是被加載的,如果設(shè)置成功,則把這個時間觸發(fā)發(fā)生器(TimerPulse)加入到觸發(fā)源(TimerPulseSource)中用來放置TimerPulse的堆結(jié)構(gòu)(Heap)中去。當(dāng)時間出發(fā)發(fā)生器(TimerPulse)放到Heap中去以后(會根據(jù)下一次觸發(fā)事件進(jìn)行排序),當(dāng)下一次觸發(fā)時間快到的時候,我們就需要把它從Heap中取出來,去生成其對應(yīng)的觸發(fā)器關(guān)聯(lián)的后臺事務(wù)實例(Job)

22、。這是通過TimerHandler類來實現(xiàn)的,這個類也實現(xiàn)IJobHandler, IcoreJobHandler接口,是專門用來處理時間觸發(fā)發(fā)生器的,在服務(wù)器啟動初始化TimerPulseSource的時候,就新建了一個后臺事務(wù)實例,并添加到了就緒隊列或等待隊列中去了。JobInstanceConfig cfg=new JobInstanceConfig(new TimerHandler(this), false, true, 0);Job job=new Job(null,"Timer pulse source!", cfg, Boolean.TRUE);JobMana

23、ger.instance().add(job);而且這個任務(wù)每隔一秒會被執(zhí)行一次,因為execute方法返回值為Delay(1)。而且在execute方法中調(diào)用的是schedule()方法。private void schedule()Date now=new Timestamp(System.currentTimeMillis()+Configuration.persistantJobCheckInterval()*1000);while(true)TimerPulse pulse=pulseSource.top();if(pulse=null)return;if(pulse.nextFir

24、eTime().after(now)return;pulse=pulseSource.pop();if(pulse.fire()pulseSource.add(pulse);該方法的作用是:首先設(shè)定一個時間,該時間是距離現(xiàn)在還有一個持久化任務(wù)加載間隔時間的未來時間,先讀取堆中的堆頂元素,看下一次觸發(fā)時間是否晚于設(shè)定的那個時間,如果是,就直接返回,如果不是,就把堆頂元素pop出來,并觸發(fā)時間觸發(fā)發(fā)生器(TimerPulse),即調(diào)用了fire()方法。public boolean fire() if(!trigger.triggered(nextFireTime)return false;/觸發(fā)

25、失敗,觸發(fā)發(fā)生器應(yīng)失效 lastTriggeredTime=nextFireTime; nextFireTime=quartzTrigger.getFireTimeAfter(lastTriggeredTime); boolean b=nextFireTime!=null; if(!b)trigger.inValid();/以后不需觸發(fā),觸發(fā)發(fā)生器應(yīng)失效return b;public boolean triggered(Date lastTriggeredTime)tryJob job=newJob(lastTriggeredTime);if(!modifyState(lastTriggere

26、dTime)return false;JobManager.instance().add(job);return true;catch(Throwable t) 。(省略)從上面兩個方法知,TimerPulse雷 中的fire()方法調(diào)用了Trigger類中triggered(Date lastTriggeredTime)方法,在triggered方法中,由下一次觸發(fā)時間得到了這個觸發(fā)器關(guān)聯(lián)的后臺事務(wù)實例對象(Job),modifyState()方法是同步數(shù)據(jù)庫,更新該觸發(fā)器的觸發(fā)次數(shù)和最后一次觸發(fā)時間等信息。如果更新成功則把得到的后臺事務(wù)實例加入到等待或就緒隊列中去。fire()在執(zhí)行完tr

27、iggered方法后,會更新一下自身(TimerPulse)的最后一次觸發(fā)時間和下一次觸發(fā)時間。如果fire()方法返回值為true,則把這個更新好的TimerPulse又放入到堆(Heap)結(jié)構(gòu)中去,等待下一次調(diào)度。注意:在使用任務(wù)管理器JobManage提交后臺事務(wù)實例(Job)中,會調(diào)用job.setState(JobState.Created)語句,該語句會根據(jù)后臺事務(wù)實例是否是可持久化事物進(jìn)行判斷,如果是持久化任務(wù)就把它保存到數(shù)據(jù)庫中去,并設(shè)置為“創(chuàng)建”的狀態(tài),如果是非持久化任務(wù)就直接設(shè)置其狀態(tài)為“創(chuàng)建”,不做其他任何操作。接著再一次進(jìn)行判斷,如果是持久化任務(wù)則返回,因為持久化任務(wù)實

28、例已經(jīng)保存到數(shù)據(jù)庫中去了,此時需要通過任務(wù)加載器(JobLoader)從數(shù)據(jù)庫進(jìn)行加載。非持久化任務(wù)就可以被加入到等待或就緒隊列中去了。if(!job.setState(JobState.Created) 。(省略)/持久化任務(wù)由任務(wù)加載器從數(shù)據(jù)庫加載if(job.isPersistent()return;if(job.getScheduledTime().after(new Date()b=WaitingJobs.in(job);elseb=ReadyJobs.in(job);持久化任務(wù)加載器(JobLoader)實現(xiàn)了IJobHandler,IcoreJobHandler接口,該類在啟動服

29、務(wù)器的時候就被初始化了,開啟了一個加載后臺事務(wù)實例加載事務(wù),該事務(wù)會不行地執(zhí)行,因為該任務(wù)處理器對應(yīng)的execuse()方法返回值為new Delay(t);在execuse()方法中調(diào)用了load()方法,該方法就是從數(shù)據(jù)庫表T_JOB_INST中取得計劃執(zhí)行時間快到得后臺事務(wù)實例(Job)。并直接放到等待隊列或就緒隊列中去。到現(xiàn)在為止,后臺事務(wù)實例(Job)已經(jīng)都已經(jīng)放到后臺事務(wù)等待隊列或就緒隊列中去了,還要完成的工作就是按照后臺事務(wù)實例的計劃執(zhí)行時間(scheduledTime)來從就緒隊列中取出,并執(zhí)行這個后臺事務(wù)實例(Job)了。處理這項工作的是負(fù)責(zé)后臺事務(wù)執(zhí)行的工作線程類Threa

30、dWorker,在服務(wù)器啟動的時候會進(jìn)行初始化,新建配置文件上指定個數(shù)的工作線程,然后調(diào)用start方法啟動這些線程。下面是線程對象的run()方法。public void run() Job job;boolean executed;while(true)/服務(wù)停止則退出線程if(!BOSSchedulerService.isRunning()break;job=null;executed=false;try/獲取下一個任務(wù)job=ReadyJobs.out();if(!RunningJobs.put(job)job=null;continue;/檢查是否錯過最后執(zhí)行期限D(zhuǎn)ate e=job

31、.getExpiredTime();if(e!=null)if(System.currentTimeMillis()>e.getTime()job.setState(JobState.Missed);continue;/執(zhí)行executed=execute(job);catch(Throwable t)log.error("execute job "+job+" failed!",t);finally/釋放任務(wù)RunningJobs.dispose(job);if(executed)/如果任務(wù)被成功執(zhí)行try/調(diào)整任務(wù)定義屬性adjustJobDe

32、f(job);/執(zhí)行后處理handleReturns(job);catch(Throwable t)log.error("handler job "+job+"'s returns failed!",t);從這個方法可以看出,首先從就緒隊列中取出一個任務(wù),然后放入到放到運行時任務(wù)中,檢查是否錯過最后期限,這個值跟錯過策略設(shè)置有關(guān),如果設(shè)置為錯過“立刻執(zhí)行”,則這個值為null,如果為“忽略不計”則是在程序中設(shè)置的一個時間。如果設(shè)置為“忽略不計”,且執(zhí)行最后期限已過,則設(shè)置為“已錯過”的狀態(tài)。接著就執(zhí)行這個任務(wù),在這個過程中要完成設(shè)置任務(wù)執(zhí)行狀態(tài),

33、觸發(fā)一些事件,以及調(diào)用該任務(wù)的任務(wù)處理器對該任務(wù)進(jìn)行處理。任務(wù)執(zhí)行完以后,需要釋放這個任務(wù),并且調(diào)整任務(wù)定義的屬性和執(zhí)行一些后續(xù)處理。這樣,就整個任務(wù)就執(zhí)行完畢了。對持久化后臺事務(wù)會產(chǎn)生一個后臺事務(wù)實例,我們可以通過“后臺事務(wù)實例”按鈕進(jìn)行查看,執(zhí)行失敗的后臺事務(wù)實例會產(chǎn)生日志信息,我們通過日志信息可以很快定位到執(zhí)行后臺事務(wù)失敗的原因。3. 代碼實現(xiàn)后臺事務(wù)定義3.1. 非持久化后臺事務(wù)在登陸EAS系統(tǒng)后,進(jìn)入后臺事務(wù)管理,在臺事務(wù)定義的界面上我們是不能選擇是定義持久化事務(wù)和非持久化事務(wù)的,通過在EAS系統(tǒng)中定義的后臺事務(wù)默認(rèn)都是持久化事務(wù)的。如果我們要定義非持久化后臺事務(wù),就必須通過代碼來實

34、現(xiàn)。通過對前一章的學(xué)習(xí),我們已經(jīng)熟悉后臺事務(wù)執(zhí)行的過程,我們可以在服務(wù)器端程序代碼中新建一個后臺事務(wù)定義(JobDef)的對象,再調(diào)用JobService的createJobDef(JobDef def)方法就可以創(chuàng)建一個后臺事務(wù)定義了。下面是我在后臺事務(wù)客戶端Façade的JobServiceUIFacade類中定義的一個方法。其中TestJob是一個任務(wù)處理類,實現(xiàn)了IJobHandler接口,可以通過在這個類中的execute()方法中實現(xiàn)自己的測試業(yè)務(wù)邏輯。public void createLogJobDef() throws BOSExceptionString pack

35、ageName = null;String title= "后臺事務(wù)日志記錄測試"String description = "測試要求"String proxyUserID=null;String proxyOrgID=null;boolean isPersistent=false;Wrapper wrapper=new InterfaceWrapper(new TestJob();JobDef def=new JobDef(ctx,packageName, title, description,proxyUserID, proxyOrgID, isPe

36、rsistent, wrapper);JobServiceFactory.getLocalInstance(ctx).createJobDef(def);通過設(shè)置其isPersistent屬性為false,這樣創(chuàng)建的后臺事務(wù)定義是一個非持久化后臺事務(wù)定義。調(diào)用了JobService的createJobDef(def)方法,而這個方法又是通過反射調(diào)用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法,在這個方法會調(diào)用def.save(),即對我定義的后臺事務(wù)定義JobDef進(jìn)行數(shù)據(jù)庫保存。這里面的trig

37、ger為空,即該后臺事務(wù)沒有關(guān)聯(lián)觸發(fā)器。我們需要執(zhí)行這個方法,使之產(chǎn)生的后臺事務(wù)定義可以在后臺事務(wù)定義報表中顯示出來,在這里,我是放在這個類的testJobDef方法中讓它執(zhí)行的。public String testJobDef(ProcessDef process) throws BOSException JobDef def=JobServiceUtil.toJobDef(ctx, process);createLogJobDef();/測試日志記錄后臺事務(wù)return JobServiceFactory.getLocalInstance(ctx).testJobDef(def, null

38、);這個方法關(guān)聯(lián)到后臺事務(wù)定義界面上的“測試”按鈕,所以只要點擊那個按鈕,就會執(zhí)行我這個方法,從而產(chǎn)生一個后臺事務(wù)定義對象并保存到數(shù)據(jù)庫中,我們就可以在后臺事務(wù)定義報表中看到我們定義的這條記錄。由于這個后臺事務(wù)沒有關(guān)聯(lián)觸發(fā)器,所以要通過點擊“啟動”按鈕觸發(fā)后臺事務(wù),提示后臺事務(wù)啟動成功后,點“后臺事務(wù)實例”按鈕,可以看到不會產(chǎn)生任務(wù)實例。這個后臺事務(wù)完成的工作仍然是向D盤目錄下的fileLog.txt文件中寫時間,在測試之前可以先刪除這個文件,測試后如果產(chǎn)生這個文件并記錄下了當(dāng)前時間則說明執(zhí)行成功。3.2. 錯過策略在后臺事務(wù)定義的時候,可以進(jìn)行設(shè)置后臺事務(wù)的錯過策略,有兩種情況,一種是“立即

39、執(zhí)行”,另一種是“忽略不計”,其中這兩種是怎樣工作的呢?在后臺事務(wù)定義的時候,后臺事務(wù)錯過策略信息是保存在T_JOB_DEF的fmissedtimeout字段中,設(shè)置后臺事務(wù)的錯過策略為立即執(zhí)行,這個字段保存的值為-1,設(shè)置為忽略不計,這個字段保存的值為0;在代碼中是如何設(shè)置其錯過的策略呢?通過下面這段代碼你就可以明白系統(tǒng)是怎樣進(jìn)行處理的。if(scheduledTime=null)scheduledTime=new Timestamp(System.currentTimeMillis();if(expiredTime=null)int timeout=this.missedTimeout;i

40、nt t=Configuration.persistantJobCheckInterval()*10;if(timeout=0)/默認(rèn)錯過超時, 取任務(wù)加載間隔的10倍, 且該值不得小于180秒timeout=t;if(timeout<180)timeout=180;if(timeout>0)if(timeout<t)timeout=t;expiredTime=new Timestamp(scheduledTime.getTime()+timeout*1000);從上面可以看出,如果計劃執(zhí)行時間為空,則設(shè)置計劃執(zhí)行時間為當(dāng)前時間,如果過期時間為空,則首先判斷JobDef的mi

41、ssedTimeout是否為0,如果為零,表示忽略不計,對忽略不計這種情況,系統(tǒng)會默認(rèn)設(shè)置它的過期時間,如果超過這個過期時間,就設(shè)置這個后臺事務(wù)實例的狀態(tài)為“已錯過”,對于立即執(zhí)行這種情況,就不會去設(shè)置它的過期時間,expiredTime這個值為null,表示永不過期。在后臺事務(wù)執(zhí)行的時候,會檢查后臺事務(wù)實例的expiredTime值,如果不為空且現(xiàn)在時間已經(jīng)過了這個時間就會設(shè)置其狀態(tài)為“已錯過”。其判斷代碼如下(在ThreadWorker的run()方法中):Date e=job.getExpiredTime();if(e!=null)if(System.currentTimeMillis(

42、)>e.getTime()job.setState(JobState.Missed);continue;通常在多個線程同時運行的情況下,我們運行的后臺事務(wù)實例不會因為阻塞而產(chǎn)生“已錯過的情況”,如果我們需要對這種情況進(jìn)行測試,這就需要我們在程序代碼中進(jìn)行控制,我們可以在后臺任務(wù)管理器JobManager中的if(job.getScheduledTime().after(new Date()語句上設(shè)置一個斷點,阻塞其運行,待產(chǎn)生的后臺事務(wù)實例的最后期限已經(jīng)過去,再去掉斷點,讓它們繼續(xù)運行,這樣我們就可以看到我們產(chǎn)生的后臺實例出現(xiàn)“已錯過”的情況。同樣,我們可以進(jìn)行錯過策略為“立刻執(zhí)行”的測

43、試,不管我們的斷點阻塞線程運行多久,只要我們一讓它運行,后臺事務(wù)仍然會執(zhí)行,如下圖所示,開始執(zhí)行時間比計劃執(zhí)行時間晚了3分多種,總而言之,選擇“立刻執(zhí)行”的后臺事務(wù)實例,只要讓它獲得執(zhí)行的機會就會直接執(zhí)行。3.3. 人工干預(yù)(撤銷,掛起,喚醒)對已接收,重新調(diào)度,已就緒,等待中的后臺事務(wù)實例可進(jìn)行撤銷和掛起操作,對已掛起的后臺事務(wù)實例可以進(jìn)行喚醒操作,已錯過的后臺事務(wù)可以進(jìn)行修復(fù)操作(其實質(zhì)也就是重新調(diào)度),對運行中的后臺事務(wù)不能夠做任何操作,這些情況在實際運行中可能不是很好把握,因為從一個轉(zhuǎn)臺轉(zhuǎn)換到另一個狀態(tài)轉(zhuǎn)換的時間間隔很少,所以要出現(xiàn)下面這些情況,最好還是通過設(shè)置斷點,在不同的時機進(jìn)行阻

44、塞操作。3.4. 調(diào)度計劃時間后臺事務(wù)觸發(fā)器的調(diào)度計劃時間表達(dá)式是保存在數(shù)據(jù)庫表T_JOB_TRIGGER的fschedulePlan字段中,String schedulePlan=schedule.getType().toString()+":"+schedule.getDefine();由這條語句可知,其值由兩部分組成。分別為調(diào)度類別和調(diào)度的定義,其中調(diào)度定義是Quartz的cron 表達(dá)式的格式,后臺事務(wù)獲取下一次觸發(fā)時間就是根據(jù)這個表達(dá)式來得到的,首先根據(jù)這個表達(dá)式可以新建一個CronTrigger對象,通過這個對象及上一次的觸發(fā)時間就可以得到下一次觸發(fā)時間了:qu

45、artzTrigger.getFireTimeAfter(lastTriggeredTime)。下面是對Quartz的cron 表達(dá)式的格式的說明:Quartz 用 cron 表達(dá)式存放執(zhí)行計劃。引用了 cron 表達(dá)式的 CronTrigger 在計劃的時間里會與 job 關(guān)聯(lián)上。Quartz 提供七個域。下表列出了 Quartz cron 表達(dá)式支持的七個域。名稱是否必須允許值特殊字符秒是0-59, - * /分是0-59, - * /時是0-23, - * /日是1-31, - * ? / L W C月是1-12 或 JAN-DEC, - * /周是1-7 或 SUN-SAT, - *

46、? / L C #年否空 或 1970-2099, - * /月份和星期的名稱是不區(qū)分大小寫的。FRI 和 fri 是一樣的。域之間有空格分隔,這和 UNIX cron 一樣。無可爭辯的,我們能寫的最簡單的表達(dá)式看起來就是這個了:* * * ? * *,這個表達(dá)會每秒鐘(每分種的、每小時的、每天的)激發(fā)一個部署的 job。Quartz cron 表達(dá)式支持用特殊字符來創(chuàng)建更為復(fù)雜的執(zhí)行計劃:*星號:使用星號(*) 指示著你想在這個域上包含所有合法的值。例如,在月份域上使用星號意味著每個月都會觸發(fā)這個 trigger。表達(dá)式樣例:0 * 17 * * ?意義:每天從下午5點到下午5:59中的每分鐘激發(fā)一次 trigger。它停在下午 5:59 是因為值 17 在小時域上,在下午 6 點時,小時變?yōu)?18 了,也就不再理會這個 trigger,直到下一天的下午5點,在你希望 trigger 在該域的所有有效值上被激發(fā)時使用 * 字符。? 問號:? 號只能用在日和周域上,但是不能在這兩個域上同時使用。你可以認(rèn)為 ? 字符是 "我并不關(guān)心在該域上是什么值。" 這不同于星號,星號是指示著該域上的每一個值。? 是說不為該域指定值。不能同時這兩個域上指定值的理由是難以解釋甚至是難以理解的。基本上,假定同時指定值的話,意義就會變得含混不清了:考慮一下,如果一個表達(dá)式在日域上

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論