




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、后臺(tái)事務(wù)開發(fā)文檔目 錄1.簡(jiǎn)單后臺(tái)事務(wù)示例32.后臺(tái)事務(wù)執(zhí)行過(guò)程及原理132.1.后臺(tái)事務(wù)定義132.2.后臺(tái)事務(wù)發(fā)布142.3.后臺(tái)事務(wù)執(zhí)行153.代碼實(shí)現(xiàn)后臺(tái)事務(wù)定義223.1.非持久化后臺(tái)事務(wù)223.2.錯(cuò)過(guò)策略253.3.人工干預(yù)(撤銷,掛起,喚醒)283.4.調(diào)度計(jì)劃時(shí)間331. 簡(jiǎn)單后臺(tái)事務(wù)示例在進(jìn)行后臺(tái)事務(wù)示例測(cè)試之前,我們需要做一些準(zhǔn)備工作,首先,我們要在BOS Studio的BIM視圖下新建幾個(gè)元數(shù)據(jù),如圖1所示:如上圖所示,新建了四個(gè)元數(shù)據(jù)(其中T_BO_BandOffice.table是根據(jù)實(shí)體右鍵導(dǎo)出表直接導(dǎo)出得到的),其實(shí)這四個(gè)元數(shù)據(jù)很簡(jiǎn)單,F(xiàn)ileLogFacade
2、.facade只有一個(gè)方法logWriter(String str);該方法實(shí)現(xiàn)了向服務(wù)器上的C:/file.log輸入日志。BandOffice.entity上新建了一個(gè)方法,該方法和FileLogFacade.facade的方法logWriter(String str)作用一樣,名字為testLog(String str),只是為了示范兩個(gè)不同的調(diào)用。實(shí)體BandOffice.entity還新建了一個(gè)TestLogEvent的事件,該事件可以引用實(shí)體本身的方法logWriter,也可以引用功能FileLogFacade的testLog方法。再定義了一個(gè)業(yè)務(wù)功能FileLogFunction
3、,在這個(gè)業(yè)務(wù)功能中定義了一個(gè)操作fileLog;通過(guò)應(yīng)用,與實(shí)體上的事件關(guān)聯(lián)起來(lái)。在定義好這些元數(shù)據(jù)后,我們需要把這些元數(shù)據(jù)進(jìn)行發(fā)布,使服務(wù)器端在運(yùn)行時(shí)能夠加載這些元數(shù)據(jù)。上面是我們對(duì)解決方案的發(fā)布方案設(shè)置,如紅框標(biāo)識(shí)的,我把元數(shù)據(jù)發(fā)布后生成的代碼放在W:workspacebs_jobdevtest目錄中,如下圖所示,元數(shù)據(jù)發(fā)布后在這個(gè)目錄下生成的代碼,這是實(shí)體和功能(facade)發(fā)布時(shí)生成的代碼,其他元數(shù)據(jù)發(fā)布時(shí)不生成代碼。在FileLogFacadeControllerBean中會(huì)生成功能定義的_logWriter方法,通過(guò)在這個(gè)方法中寫邏輯即可實(shí)現(xiàn),而實(shí)體定義的方法會(huì)在BandOffi
4、ceControllerBean中生成_testLog方法。元數(shù)據(jù)發(fā)布后,我們需要重新生成系統(tǒng)子樹,這樣我們?cè)谥貑⒎?wù)器后可以在事務(wù)任務(wù)選擇樹中看到我們定義的功能。具體生成系統(tǒng)子樹的方法,請(qǐng)參照生成系統(tǒng)子樹的兩種方法.doc。登陸EAS后,我們選擇系統(tǒng)平臺(tái)>后臺(tái)事務(wù)定義,進(jìn)入后臺(tái)事務(wù)定義界面:接著我們就按要求進(jìn)行定義,下面我們定義調(diào)度計(jì)劃設(shè)置:定義好后,我們進(jìn)行保存,這個(gè)時(shí)候我們可以進(jìn)行發(fā)布了,如下圖:我們?cè)龠M(jìn)入:系統(tǒng)平臺(tái)>后臺(tái)事務(wù)定義表,我們可以看到我們剛才定義的那個(gè)事務(wù)了我們?cè)O(shè)置好了調(diào)度計(jì)劃,當(dāng)時(shí)間到達(dá)時(shí),觸發(fā)該事務(wù),就會(huì)執(zhí)行一個(gè)事務(wù)實(shí)例,我們可以查看該事務(wù)實(shí)例,我們也可以直接
5、點(diǎn)擊工具欄中的啟動(dòng),這樣會(huì)立刻啟動(dòng)一個(gè)事務(wù)實(shí)例。從上面可以看出,事務(wù)執(zhí)行成功。我們?cè)賮?lái)看看我們實(shí)現(xiàn)的功能,就是在服務(wù)器目錄下寫入一條時(shí)間信息記錄:2. 后臺(tái)事務(wù)執(zhí)行過(guò)程及原理首先我們要弄清楚,在EAS系統(tǒng)的后臺(tái)事務(wù)管理平臺(tái)中定義一個(gè)后臺(tái)事務(wù)的過(guò)程是怎樣的,你可以通過(guò)對(duì)代碼的跟蹤進(jìn)行了解,2.1. 后臺(tái)事務(wù)定義首先我們?cè)诤笈_(tái)事務(wù)定義界面上新建一個(gè)后臺(tái)事務(wù)(如下圖所示),這些后臺(tái)事務(wù)定義信息會(huì)被保存在T_WFD_PROCESSDEF中,可以在這個(gè)界面對(duì)已經(jīng)定義好的后臺(tái)事務(wù)定義進(jìn)行修改和刪除,當(dāng)我們選擇左邊系統(tǒng)子樹的某個(gè)后臺(tái)事務(wù)定義節(jié)點(diǎn),它對(duì)應(yīng)的信息就會(huì)從該表中獲得并顯示在右邊的界面上。現(xiàn)在我想描述
6、一下右邊這顆系統(tǒng)子樹的生成過(guò)程。這可系統(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中有三個(gè)靜態(tài)變量,分別為PROCESS,F(xiàn)UNCTION,ENTITY,分別用來(lái)生成不同的系統(tǒng)子樹,在對(duì)系統(tǒng)子樹進(jìn)行初始化調(diào)用了兩個(gè)方法,第一個(gè)是initialPackageTree(tree ,flag);這個(gè)方法是獲得標(biāo)準(zhǔn)包結(jié)構(gòu)的樹,第二個(gè)是initalFunctionOrJobProcessTree(tre
8、e,flag);這個(gè)是獲得客戶化的樹形結(jié)構(gòu),通過(guò)該方法可以在標(biāo)準(zhǔn)化得包結(jié)構(gòu)下添加功能節(jié)點(diǎn)或后臺(tái)事務(wù)定義節(jié)點(diǎn)。如果是生成功能(function)子樹或?qū)嶓w(entity)子樹,這就得獲取這些元數(shù)據(jù)的集合,而它們對(duì)應(yīng)的集合類:FunctionObjectCollection和EntityObjectCollection,獲取這些集合的方法為:IMetaDataLoader loader = MetaDataLoaderFactory. getRemoteMetaDataLoader()FunctionObjectCollection funcol = loader.getFunctions()En
9、tityObjectCollection entcol = loader.getEntityCollection();2.2. 后臺(tái)事務(wù)發(fā)布后臺(tái)事務(wù)定義完成以后,通過(guò)發(fā)布,我們就可以在后臺(tái)事務(wù)定義報(bào)表看到我們定義的后臺(tái)事務(wù)了,已發(fā)布的后臺(tái)事務(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);后臺(tái)事務(wù)定義被發(fā)布以后,產(chǎn)生了一個(gè)新的對(duì)象JobDef,對(duì)用在數(shù)據(jù)庫(kù)中的表是T_JOB_DEF。后臺(tái)事務(wù)實(shí)例(Job)就是根據(jù)這個(gè)定義來(lái)產(chǎn)生的。由上面的代碼可知,后臺(tái)事務(wù)定義(JobDef)和觸發(fā)器(Trigger)是由流程定義(ProcessDef)轉(zhuǎn)換過(guò)來(lái)的,這是因?yàn)樵谝郧暗暮笈_(tái)事務(wù)是按照工作流的方式進(jìn)行處理的,每一個(gè)后臺(tái)事務(wù)定義都會(huì)生成一個(gè)流程定義保存在數(shù)據(jù)庫(kù)表(T_WFD_PROCESSDEF)的一個(gè)字段中,我
11、們用代碼實(shí)現(xiàn)后臺(tái)事務(wù)定義就不必通過(guò)這樣獲取流程定義(JobDef)。有上面代碼可知,通過(guò)調(diào)用JobService的createJobDef(JobDef def, Trigger trigger, boolean enable);方法創(chuàng)建后臺(tái)事務(wù)定義(實(shí)際上是通過(guò)反射調(diào)用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法),通過(guò)該方法將JobDef,Trigger對(duì)象保存到數(shù)據(jù)庫(kù)中的T_JOB_DEF和T_TRIGGER表中。從而完成了后臺(tái)事務(wù)定義的發(fā)布功能。2.3. 后臺(tái)事務(wù)執(zhí)行后臺(tái)事務(wù)每執(zhí)行一次就會(huì)
12、產(chǎn)出一個(gè)后臺(tái)事務(wù)實(shí)例(Job),持久化后臺(tái)事務(wù)會(huì)將后臺(tái)事務(wù)實(shí)例信息保存在T_JOB_INST表中。 我們可以通過(guò)兩種方式來(lái)觸發(fā)后臺(tái)事務(wù)執(zhí)行,第一種是通過(guò)后臺(tái)事務(wù)界面上的“啟動(dòng)”按鈕直接觸發(fā)產(chǎn)生一個(gè)后臺(tái)事務(wù)實(shí)例,第二種是通過(guò)定義一個(gè)觸發(fā)器(Trigger)來(lái)與該后臺(tái)事務(wù)定義關(guān)聯(lián)起來(lái),用觸發(fā)器來(lái)觸發(fā)產(chǎn)生一個(gè)后臺(tái)事務(wù)實(shí)例。下面我們分別來(lái)討論通過(guò)這兩種方法是怎樣觸發(fā)產(chǎn)生后臺(tái)事務(wù)實(shí)例的。“啟動(dòng)”按鈕觸發(fā)后臺(tái)事務(wù)。當(dāng)我們點(diǎn)擊“啟動(dòng)”按鈕會(huì)觸發(fā)該按鈕的事件,會(huì)調(diào)用到該界面JobProcessDefineListUI的actionStart_actionPerformed方法,通過(guò)方法調(diào)用,我們可以知道它調(diào)用
13、了JobServiceUIFacade的createJobInstance(jobDefId, null, null),在這個(gè)類中,通過(guò)獲取到了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、個(gè)方法可以看出,首先由jobDefId獲取到JobDef對(duì)象def,在設(shè)置這個(gè)產(chǎn)生的任務(wù)實(shí)例的計(jì)劃執(zhí)行時(shí)間為當(dāng)前時(shí)間的下一秒。然后由后臺(tái)事務(wù)定義對(duì)象def的newInstance(ctx, param, scheduledTime)產(chǎn)生一個(gè)后臺(tái)事務(wù)實(shí)例對(duì)象job,再由后臺(tái)事務(wù)管理器JobManager(單子模式)調(diào)用add(job)方法把這個(gè)任務(wù)實(shí)例加載到等待隊(duì)列或者就需隊(duì)列中去;觸發(fā)器觸發(fā)后臺(tái)事務(wù)。如果我們?cè)诤笈_(tái)事務(wù)定義的時(shí)候?qū)ζ潢P(guān)聯(lián)了相應(yīng)的觸發(fā)器,則當(dāng)下一次觸發(fā)時(shí)間到達(dá)時(shí),觸發(fā)器就會(huì)觸發(fā)與之關(guān)聯(lián)的后臺(tái)事務(wù)定義(JobDef)產(chǎn)生一個(gè)后臺(tái)事務(wù)實(shí)例(Job)。觸發(fā)器觸發(fā)后臺(tái)事務(wù)的過(guò)程是怎樣的呢
16、?首先:服務(wù)器啟動(dòng)時(shí)會(huì)初始化觸發(fā)器加載類(TriggerLoader),該類實(shí)現(xiàn)了IJobHandler, IcoreJobHandler兩個(gè)接口,本身就是一個(gè)任務(wù)處理器,這個(gè)類的作用是:觸發(fā)器加載者,定期從數(shù)據(jù)庫(kù)加載新增的觸發(fā)器。其load()方法會(huì)定期被調(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");/無(wú)持有者/只加載已生效的觸發(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;這個(gè)方法實(shí)現(xiàn)了從數(shù)據(jù)庫(kù)表T_JOB_TRIGGER中加載新添加的觸發(fā)器,并返回觸發(fā)器加載時(shí)間間隔(上面設(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、是通過(guò)一個(gè)觸發(fā)器新建一個(gè)時(shí)間觸發(fā)發(fā)生器(TimerPulse),如果獲取不到這個(gè)TimerPulse下一次觸發(fā)時(shí)間,就禁用這個(gè)觸發(fā)器(Trigger),否則設(shè)置這個(gè)TimerPulse對(duì)應(yīng)的觸發(fā)器(Trigger)的狀態(tài)是被加載的,如果設(shè)置成功,則把這個(gè)時(shí)間觸發(fā)發(fā)生器(TimerPulse)加入到觸發(fā)源(TimerPulseSource)中用來(lái)放置TimerPulse的堆結(jié)構(gòu)(Heap)中去。當(dāng)時(shí)間出發(fā)發(fā)生器(TimerPulse)放到Heap中去以后(會(huì)根據(jù)下一次觸發(fā)事件進(jìn)行排序),當(dāng)下一次觸發(fā)時(shí)間快到的時(shí)候,我們就需要把它從Heap中取出來(lái),去生成其對(duì)應(yīng)的觸發(fā)器關(guān)聯(lián)的后臺(tái)事務(wù)實(shí)例(Job)
22、。這是通過(guò)TimerHandler類來(lái)實(shí)現(xiàn)的,這個(gè)類也實(shí)現(xiàn)IJobHandler, IcoreJobHandler接口,是專門用來(lái)處理時(shí)間觸發(fā)發(fā)生器的,在服務(wù)器啟動(dòng)初始化TimerPulseSource的時(shí)候,就新建了一個(gè)后臺(tái)事務(wù)實(shí)例,并添加到了就緒隊(duì)列或等待隊(duì)列中去了。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);而且這個(gè)任務(wù)每隔一秒會(huì)被執(zhí)行一次,因?yàn)閑xecute方法返回值為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è)定一個(gè)時(shí)間,該時(shí)間是距離現(xiàn)在還有一個(gè)持久化任務(wù)加載間隔時(shí)間的未來(lái)時(shí)間,先讀取堆中的堆頂元素,看下一次觸發(fā)時(shí)間是否晚于設(shè)定的那個(gè)時(shí)間,如果是,就直接返回,如果不是,就把堆頂元素pop出來(lái),并觸發(fā)時(shí)間觸發(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) 。(省略)從上面兩個(gè)方法知,TimerPulse雷 中的fire()方法調(diào)用了Trigger類中triggered(Date lastTriggeredTime)方法,在triggered方法中,由下一次觸發(fā)時(shí)間得到了這個(gè)觸發(fā)器關(guān)聯(lián)的后臺(tái)事務(wù)實(shí)例對(duì)象(Job),modifyState()方法是同步數(shù)據(jù)庫(kù),更新該觸發(fā)器的觸發(fā)次數(shù)和最后一次觸發(fā)時(shí)間等信息。如果更新成功則把得到的后臺(tái)事務(wù)實(shí)例加入到等待或就緒隊(duì)列中去。fire()在執(zhí)行完tr
27、iggered方法后,會(huì)更新一下自身(TimerPulse)的最后一次觸發(fā)時(shí)間和下一次觸發(fā)時(shí)間。如果fire()方法返回值為true,則把這個(gè)更新好的TimerPulse又放入到堆(Heap)結(jié)構(gòu)中去,等待下一次調(diào)度。注意:在使用任務(wù)管理器JobManage提交后臺(tái)事務(wù)實(shí)例(Job)中,會(huì)調(diào)用job.setState(JobState.Created)語(yǔ)句,該語(yǔ)句會(huì)根據(jù)后臺(tái)事務(wù)實(shí)例是否是可持久化事物進(jìn)行判斷,如果是持久化任務(wù)就把它保存到數(shù)據(jù)庫(kù)中去,并設(shè)置為“創(chuàng)建”的狀態(tài),如果是非持久化任務(wù)就直接設(shè)置其狀態(tài)為“創(chuàng)建”,不做其他任何操作。接著再一次進(jìn)行判斷,如果是持久化任務(wù)則返回,因?yàn)槌志没蝿?wù)實(shí)
28、例已經(jīng)保存到數(shù)據(jù)庫(kù)中去了,此時(shí)需要通過(guò)任務(wù)加載器(JobLoader)從數(shù)據(jù)庫(kù)進(jìn)行加載。非持久化任務(wù)就可以被加入到等待或就緒隊(duì)列中去了。if(!job.setState(JobState.Created) 。(省略)/持久化任務(wù)由任務(wù)加載器從數(shù)據(jù)庫(kù)加載if(job.isPersistent()return;if(job.getScheduledTime().after(new Date()b=WaitingJobs.in(job);elseb=ReadyJobs.in(job);持久化任務(wù)加載器(JobLoader)實(shí)現(xiàn)了IJobHandler,IcoreJobHandler接口,該類在啟動(dòng)服
29、務(wù)器的時(shí)候就被初始化了,開啟了一個(gè)加載后臺(tái)事務(wù)實(shí)例加載事務(wù),該事務(wù)會(huì)不行地執(zhí)行,因?yàn)樵撊蝿?wù)處理器對(duì)應(yīng)的execuse()方法返回值為new Delay(t);在execuse()方法中調(diào)用了load()方法,該方法就是從數(shù)據(jù)庫(kù)表T_JOB_INST中取得計(jì)劃執(zhí)行時(shí)間快到得后臺(tái)事務(wù)實(shí)例(Job)。并直接放到等待隊(duì)列或就緒隊(duì)列中去。到現(xiàn)在為止,后臺(tái)事務(wù)實(shí)例(Job)已經(jīng)都已經(jīng)放到后臺(tái)事務(wù)等待隊(duì)列或就緒隊(duì)列中去了,還要完成的工作就是按照后臺(tái)事務(wù)實(shí)例的計(jì)劃執(zhí)行時(shí)間(scheduledTime)來(lái)從就緒隊(duì)列中取出,并執(zhí)行這個(gè)后臺(tái)事務(wù)實(shí)例(Job)了。處理這項(xiàng)工作的是負(fù)責(zé)后臺(tái)事務(wù)執(zhí)行的工作線程類Threa
30、dWorker,在服務(wù)器啟動(dòng)的時(shí)候會(huì)進(jìn)行初始化,新建配置文件上指定個(gè)數(shù)的工作線程,然后調(diào)用start方法啟動(dòng)這些線程。下面是線程對(duì)象的run()方法。public void run() Job job;boolean executed;while(true)/服務(wù)停止則退出線程if(!BOSSchedulerService.isRunning()break;job=null;executed=false;try/獲取下一個(gè)任務(wù)job=ReadyJobs.out();if(!RunningJobs.put(job)job=null;continue;/檢查是否錯(cuò)過(guò)最后執(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);從這個(gè)方法可以看出,首先從就緒隊(duì)列中取出一個(gè)任務(wù),然后放入到放到運(yùn)行時(shí)任務(wù)中,檢查是否錯(cuò)過(guò)最后期限,這個(gè)值跟錯(cuò)過(guò)策略設(shè)置有關(guān),如果設(shè)置為錯(cuò)過(guò)“立刻執(zhí)行”,則這個(gè)值為null,如果為“忽略不計(jì)”則是在程序中設(shè)置的一個(gè)時(shí)間。如果設(shè)置為“忽略不計(jì)”,且執(zhí)行最后期限已過(guò),則設(shè)置為“已錯(cuò)過(guò)”的狀態(tài)。接著就執(zhí)行這個(gè)任務(wù),在這個(gè)過(guò)程中要完成設(shè)置任務(wù)執(zhí)行狀態(tài),
33、觸發(fā)一些事件,以及調(diào)用該任務(wù)的任務(wù)處理器對(duì)該任務(wù)進(jìn)行處理。任務(wù)執(zhí)行完以后,需要釋放這個(gè)任務(wù),并且調(diào)整任務(wù)定義的屬性和執(zhí)行一些后續(xù)處理。這樣,就整個(gè)任務(wù)就執(zhí)行完畢了。對(duì)持久化后臺(tái)事務(wù)會(huì)產(chǎn)生一個(gè)后臺(tái)事務(wù)實(shí)例,我們可以通過(guò)“后臺(tái)事務(wù)實(shí)例”按鈕進(jìn)行查看,執(zhí)行失敗的后臺(tái)事務(wù)實(shí)例會(huì)產(chǎn)生日志信息,我們通過(guò)日志信息可以很快定位到執(zhí)行后臺(tái)事務(wù)失敗的原因。3. 代碼實(shí)現(xiàn)后臺(tái)事務(wù)定義3.1. 非持久化后臺(tái)事務(wù)在登陸EAS系統(tǒng)后,進(jìn)入后臺(tái)事務(wù)管理,在臺(tái)事務(wù)定義的界面上我們是不能選擇是定義持久化事務(wù)和非持久化事務(wù)的,通過(guò)在EAS系統(tǒng)中定義的后臺(tái)事務(wù)默認(rèn)都是持久化事務(wù)的。如果我們要定義非持久化后臺(tái)事務(wù),就必須通過(guò)代碼來(lái)實(shí)
34、現(xiàn)。通過(guò)對(duì)前一章的學(xué)習(xí),我們已經(jīng)熟悉后臺(tái)事務(wù)執(zhí)行的過(guò)程,我們可以在服務(wù)器端程序代碼中新建一個(gè)后臺(tái)事務(wù)定義(JobDef)的對(duì)象,再調(diào)用JobService的createJobDef(JobDef def)方法就可以創(chuàng)建一個(gè)后臺(tái)事務(wù)定義了。下面是我在后臺(tái)事務(wù)客戶端Façade的JobServiceUIFacade類中定義的一個(gè)方法。其中TestJob是一個(gè)任務(wù)處理類,實(shí)現(xiàn)了IJobHandler接口,可以通過(guò)在這個(gè)類中的execute()方法中實(shí)現(xiàn)自己的測(cè)試業(yè)務(wù)邏輯。public void createLogJobDef() throws BOSExceptionString pack
35、ageName = null;String title= "后臺(tái)事務(wù)日志記錄測(cè)試"String description = "測(cè)試要求"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);通過(guò)設(shè)置其isPersistent屬性為false,這樣創(chuàng)建的后臺(tái)事務(wù)定義是一個(gè)非持久化后臺(tái)事務(wù)定義。調(diào)用了JobService的createJobDef(def)方法,而這個(gè)方法又是通過(guò)反射調(diào)用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法,在這個(gè)方法會(huì)調(diào)用def.save(),即對(duì)我定義的后臺(tái)事務(wù)定義JobDef進(jìn)行數(shù)據(jù)庫(kù)保存。這里面的trig
37、ger為空,即該后臺(tái)事務(wù)沒有關(guān)聯(lián)觸發(fā)器。我們需要執(zhí)行這個(gè)方法,使之產(chǎn)生的后臺(tái)事務(wù)定義可以在后臺(tái)事務(wù)定義報(bào)表中顯示出來(lái),在這里,我是放在這個(gè)類的testJobDef方法中讓它執(zhí)行的。public String testJobDef(ProcessDef process) throws BOSException JobDef def=JobServiceUtil.toJobDef(ctx, process);createLogJobDef();/測(cè)試日志記錄后臺(tái)事務(wù)return JobServiceFactory.getLocalInstance(ctx).testJobDef(def, null
38、);這個(gè)方法關(guān)聯(lián)到后臺(tái)事務(wù)定義界面上的“測(cè)試”按鈕,所以只要點(diǎn)擊那個(gè)按鈕,就會(huì)執(zhí)行我這個(gè)方法,從而產(chǎn)生一個(gè)后臺(tái)事務(wù)定義對(duì)象并保存到數(shù)據(jù)庫(kù)中,我們就可以在后臺(tái)事務(wù)定義報(bào)表中看到我們定義的這條記錄。由于這個(gè)后臺(tái)事務(wù)沒有關(guān)聯(lián)觸發(fā)器,所以要通過(guò)點(diǎn)擊“啟動(dòng)”按鈕觸發(fā)后臺(tái)事務(wù),提示后臺(tái)事務(wù)啟動(dòng)成功后,點(diǎn)“后臺(tái)事務(wù)實(shí)例”按鈕,可以看到不會(huì)產(chǎn)生任務(wù)實(shí)例。這個(gè)后臺(tái)事務(wù)完成的工作仍然是向D盤目錄下的fileLog.txt文件中寫時(shí)間,在測(cè)試之前可以先刪除這個(gè)文件,測(cè)試后如果產(chǎn)生這個(gè)文件并記錄下了當(dāng)前時(shí)間則說(shuō)明執(zhí)行成功。3.2. 錯(cuò)過(guò)策略在后臺(tái)事務(wù)定義的時(shí)候,可以進(jìn)行設(shè)置后臺(tái)事務(wù)的錯(cuò)過(guò)策略,有兩種情況,一種是“立即
39、執(zhí)行”,另一種是“忽略不計(jì)”,其中這兩種是怎樣工作的呢?在后臺(tái)事務(wù)定義的時(shí)候,后臺(tái)事務(wù)錯(cuò)過(guò)策略信息是保存在T_JOB_DEF的fmissedtimeout字段中,設(shè)置后臺(tái)事務(wù)的錯(cuò)過(guò)策略為立即執(zhí)行,這個(gè)字段保存的值為-1,設(shè)置為忽略不計(jì),這個(gè)字段保存的值為0;在代碼中是如何設(shè)置其錯(cuò)過(guò)的策略呢?通過(guò)下面這段代碼你就可以明白系統(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)錯(cuò)過(guò)超時(shí), 取任務(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);從上面可以看出,如果計(jì)劃執(zhí)行時(shí)間為空,則設(shè)置計(jì)劃執(zhí)行時(shí)間為當(dāng)前時(shí)間,如果過(guò)期時(shí)間為空,則首先判斷JobDef的mi
41、ssedTimeout是否為0,如果為零,表示忽略不計(jì),對(duì)忽略不計(jì)這種情況,系統(tǒng)會(huì)默認(rèn)設(shè)置它的過(guò)期時(shí)間,如果超過(guò)這個(gè)過(guò)期時(shí)間,就設(shè)置這個(gè)后臺(tái)事務(wù)實(shí)例的狀態(tài)為“已錯(cuò)過(guò)”,對(duì)于立即執(zhí)行這種情況,就不會(huì)去設(shè)置它的過(guò)期時(shí)間,expiredTime這個(gè)值為null,表示永不過(guò)期。在后臺(tái)事務(wù)執(zhí)行的時(shí)候,會(huì)檢查后臺(tái)事務(wù)實(shí)例的expiredTime值,如果不為空且現(xiàn)在時(shí)間已經(jīng)過(guò)了這個(gè)時(shí)間就會(huì)設(shè)置其狀態(tài)為“已錯(cuò)過(guò)”。其判斷代碼如下(在ThreadWorker的run()方法中):Date e=job.getExpiredTime();if(e!=null)if(System.currentTimeMillis(
42、)>e.getTime()job.setState(JobState.Missed);continue;通常在多個(gè)線程同時(shí)運(yùn)行的情況下,我們運(yùn)行的后臺(tái)事務(wù)實(shí)例不會(huì)因?yàn)樽枞a(chǎn)生“已錯(cuò)過(guò)的情況”,如果我們需要對(duì)這種情況進(jìn)行測(cè)試,這就需要我們?cè)诔绦虼a中進(jìn)行控制,我們可以在后臺(tái)任務(wù)管理器JobManager中的if(job.getScheduledTime().after(new Date()語(yǔ)句上設(shè)置一個(gè)斷點(diǎn),阻塞其運(yùn)行,待產(chǎn)生的后臺(tái)事務(wù)實(shí)例的最后期限已經(jīng)過(guò)去,再去掉斷點(diǎn),讓它們繼續(xù)運(yùn)行,這樣我們就可以看到我們產(chǎn)生的后臺(tái)實(shí)例出現(xiàn)“已錯(cuò)過(guò)”的情況。同樣,我們可以進(jìn)行錯(cuò)過(guò)策略為“立刻執(zhí)行”的測(cè)
43、試,不管我們的斷點(diǎn)阻塞線程運(yùn)行多久,只要我們一讓它運(yùn)行,后臺(tái)事務(wù)仍然會(huì)執(zhí)行,如下圖所示,開始執(zhí)行時(shí)間比計(jì)劃執(zhí)行時(shí)間晚了3分多種,總而言之,選擇“立刻執(zhí)行”的后臺(tái)事務(wù)實(shí)例,只要讓它獲得執(zhí)行的機(jī)會(huì)就會(huì)直接執(zhí)行。3.3. 人工干預(yù)(撤銷,掛起,喚醒)對(duì)已接收,重新調(diào)度,已就緒,等待中的后臺(tái)事務(wù)實(shí)例可進(jìn)行撤銷和掛起操作,對(duì)已掛起的后臺(tái)事務(wù)實(shí)例可以進(jìn)行喚醒操作,已錯(cuò)過(guò)的后臺(tái)事務(wù)可以進(jìn)行修復(fù)操作(其實(shí)質(zhì)也就是重新調(diào)度),對(duì)運(yùn)行中的后臺(tái)事務(wù)不能夠做任何操作,這些情況在實(shí)際運(yùn)行中可能不是很好把握,因?yàn)閺囊粋€(gè)轉(zhuǎn)臺(tái)轉(zhuǎn)換到另一個(gè)狀態(tài)轉(zhuǎn)換的時(shí)間間隔很少,所以要出現(xiàn)下面這些情況,最好還是通過(guò)設(shè)置斷點(diǎn),在不同的時(shí)機(jī)進(jìn)行阻
44、塞操作。3.4. 調(diào)度計(jì)劃時(shí)間后臺(tái)事務(wù)觸發(fā)器的調(diào)度計(jì)劃時(shí)間表達(dá)式是保存在數(shù)據(jù)庫(kù)表T_JOB_TRIGGER的fschedulePlan字段中,String schedulePlan=schedule.getType().toString()+":"+schedule.getDefine();由這條語(yǔ)句可知,其值由兩部分組成。分別為調(diào)度類別和調(diào)度的定義,其中調(diào)度定義是Quartz的cron 表達(dá)式的格式,后臺(tái)事務(wù)獲取下一次觸發(fā)時(shí)間就是根據(jù)這個(gè)表達(dá)式來(lái)得到的,首先根據(jù)這個(gè)表達(dá)式可以新建一個(gè)CronTrigger對(duì)象,通過(guò)這個(gè)對(duì)象及上一次的觸發(fā)時(shí)間就可以得到下一次觸發(fā)時(shí)間了:qu
45、artzTrigger.getFireTimeAfter(lastTriggeredTime)。下面是對(duì)Quartz的cron 表達(dá)式的格式的說(shuō)明:Quartz 用 cron 表達(dá)式存放執(zhí)行計(jì)劃。引用了 cron 表達(dá)式的 CronTrigger 在計(jì)劃的時(shí)間里會(huì)與 job 關(guān)聯(lián)上。Quartz 提供七個(gè)域。下表列出了 Quartz cron 表達(dá)式支持的七個(gè)域。名稱是否必須允許值特殊字符秒是0-59, - * /分是0-59, - * /時(shí)是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 一樣。無(wú)可爭(zhēng)辯的,我們能寫的最簡(jiǎn)單的表達(dá)式看起來(lái)就是這個(gè)了:* * * ? * *,這個(gè)表達(dá)會(huì)每秒鐘(每分種的、每小時(shí)的、每天的)激發(fā)一個(gè)部署的 job。Quartz cron 表達(dá)式支持用特殊字符來(lái)創(chuàng)建更為復(fù)雜的執(zhí)行計(jì)劃:*星號(hào):使用星號(hào)(*) 指示著你想在這個(gè)域上包含所有合法的值。例如,在月份域上使用星號(hào)意味著每個(gè)月都會(huì)觸發(fā)這個(gè) trigger。表達(dá)式樣例:0 * 17 * * ?意義:每天從下午5點(diǎn)到下午5:59中的每分鐘激發(fā)一次 trigger。它停在下午 5:59 是因?yàn)橹?17 在小時(shí)域上,在下午 6 點(diǎn)時(shí),小時(shí)變?yōu)?18 了,也就不再理會(huì)這個(gè) trigger,直到下一天的下午5點(diǎn),在你希望 trigger 在該域的所有有效值上被激發(fā)時(shí)使用 * 字符。? 問(wèn)號(hào):? 號(hào)只能用在日和周域上,但是不能在這兩個(gè)域上同時(shí)使用。你可以認(rèn)為 ? 字符是 "我并不關(guān)心在該域上是什么值。" 這不同于星號(hào),星號(hào)是指示著該域上的每一個(gè)值。? 是說(shuō)不為該域指定值。不能同時(shí)這兩個(gè)域上指定值的理由是難以解釋甚至是難以理解的?;旧希俣ㄍ瑫r(shí)指定值的話,意義就會(huì)變得含混不清了:考慮一下,如果一個(gè)表達(dá)式在日域上
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國(guó)氯化膽堿行業(yè)發(fā)展分析及發(fā)展前景與投資研究報(bào)告
- 2025-2030年中國(guó)氟硅丙烯酸酯行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)核燃料行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)板材行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)木門窗行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 行政法與社會(huì)治理的互動(dòng)模式試題及答案
- 2025-2030年中國(guó)無(wú)縫管行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)旋轉(zhuǎn)機(jī)頭吹膜機(jī)行業(yè)發(fā)展分析及前景趨勢(shì)與投資研究報(bào)告
- 2025年藥師考試探討試題及答案
- 2025-2030年中國(guó)數(shù)碼產(chǎn)品設(shè)計(jì)行業(yè)市場(chǎng)發(fā)展分析及發(fā)展趨勢(shì)與投資風(fēng)險(xiǎn)研究報(bào)告
- 西安外國(guó)語(yǔ)大學(xué)
- 空間信息技術(shù)基礎(chǔ)與前沿-河南理工大學(xué)中國(guó)大學(xué)mooc課后章節(jié)答案期末考試題庫(kù)2023年
- HEYTEA喜茶品牌產(chǎn)品介紹PPT模板
- 常見顱腦急癥的CT、MRI診斷
- 各種傳染病診斷標(biāo)準(zhǔn)
- 油氣集輸管線管道工程試運(yùn)投產(chǎn)保駕方案
- 出國(guó)舉辦經(jīng)濟(jì)貿(mào)易展覽會(huì)審批管理辦法
- 2016哈弗h6運(yùn)動(dòng)版維修手冊(cè)與電路圖1406線束01機(jī)艙
- 2023年新華保險(xiǎn)校園招聘筆試參考題庫(kù)附帶答案詳解
- 高等學(xué)校英語(yǔ)應(yīng)用能力考試A級(jí)
- 新生兒心律失常課件
評(píng)論
0/150
提交評(píng)論