BOS金蝶后台事务开发文档

上传人:su****e 文档编号:202658377 上传时间:2023-04-23 格式:DOC 页数:35 大小:1.11MB
收藏 版权申诉 举报 下载
BOS金蝶后台事务开发文档_第1页
第1页 / 共35页
BOS金蝶后台事务开发文档_第2页
第2页 / 共35页
BOS金蝶后台事务开发文档_第3页
第3页 / 共35页
资源描述:

《BOS金蝶后台事务开发文档》由会员分享,可在线阅读,更多相关《BOS金蝶后台事务开发文档(35页珍藏版)》请在装配图网上搜索。

1、后台事务开发文档目 录1.简单后台事务示例32.后台事务执行过程及原理132.1.后台事务定义132.2.后台事务发布142.3.后台事务执行153.代码实现后台事务定义223.1.非持久化后台事务223.2.错过策略253.3.人工干预(撤销,挂起,唤醒)283.4.调度计划时间331. 简单后台事务示例在进行后台事务示例测试之前,我们需要做一些准备工作,首先,我们要在BOS Studio的BIM视图下新建几个元数据,如图1所示:如上图所示,新建了四个元数据(其中T_BO_BandOffice.table是根据实体右键导出表直接导出得到的),其实这四个元数据很简单,FileLogFacade

2、.facade只有一个方法logWriter(String str);该方法实现了向服务器上的C:/file.log输入日志。BandOffice.entity上新建了一个方法,该方法和FileLogFacade.facade的方法logWriter(String str)作用一样,名字为testLog(String str),只是为了示范两个不同的调用。实体BandOffice.entity还新建了一个TestLogEvent的事件,该事件可以引用实体本身的方法logWriter,也可以引用功能FileLogFacade的testLog方法。再定义了一个业务功能FileLogFunction

3、,在这个业务功能中定义了一个操作fileLog;通过应用,与实体上的事件关联起来。在定义好这些元数据后,我们需要把这些元数据进行发布,使服务器端在运行时能够加载这些元数据。上面是我们对解决方案的发布方案设置,如红框标识的,我把元数据发布后生成的代码放在W:workspacebs_jobdevtest目录中,如下图所示,元数据发布后在这个目录下生成的代码,这是实体和功能(facade)发布时生成的代码,其他元数据发布时不生成代码。在FileLogFacadeControllerBean中会生成功能定义的_logWriter方法,通过在这个方法中写逻辑即可实现,而实体定义的方法会在BandOffi

4、ceControllerBean中生成_testLog方法。元数据发布后,我们需要重新生成系统子树,这样我们在重启服务器后可以在事务任务选择树中看到我们定义的功能。具体生成系统子树的方法,请参照生成系统子树的两种方法.doc。登陆EAS后,我们选择系统平台后台事务定义,进入后台事务定义界面:接着我们就按要求进行定义,下面我们定义调度计划设置:定义好后,我们进行保存,这个时候我们可以进行发布了,如下图:我们再进入:系统平台后台事务定义表,我们可以看到我们刚才定义的那个事务了我们设置好了调度计划,当时间到达时,触发该事务,就会执行一个事务实例,我们可以查看该事务实例,我们也可以直接点击工具栏中的启

5、动,这样会立刻启动一个事务实例。从上面可以看出,事务执行成功。我们再来看看我们实现的功能,就是在服务器目录下写入一条时间信息记录:2. 后台事务执行过程及原理首先我们要弄清楚,在EAS系统的后台事务管理平台中定义一个后台事务的过程是怎样的,你可以通过对代码的跟踪进行了解,2.1. 后台事务定义首先我们在后台事务定义界面上新建一个后台事务(如下图所示),这些后台事务定义信息会被保存在T_WFD_PROCESSDEF中,可以在这个界面对已经定义好的后台事务定义进行修改和删除,当我们选择左边系统子树的某个后台事务定义节点,它对应的信息就会从该表中获得并显示在右边的界面上。现在我想描述一下右边这颗系统

6、子树的生成过程。这可系统子树是在该界面的构造函数的initializeUI()方法中进行初始化的final BusinessTreeUtil tempPTU = new BusinessTreeUtil(BusinessTreeUtil.PROCESS);(KingdeeTreeModel) this.packagekDTree.getModel().setRoot(null);JobUtils.resetStartTimeMillis();tempPTU.initialBusinessTreeView(this.packagekDTree,true);JobUtils.printTimeMi

7、llis(initialBusinessTreeView);this.packagekDTree.addTreeSelectionListener(new InnerTreeSelectionListener();BusinessTreeUtil中有三个静态变量,分别为PROCESS,FUNCTION,ENTITY,分别用来生成不同的系统子树,在对系统子树进行初始化调用了两个方法,第一个是initialPackageTree(tree ,flag);这个方法是获得标准包结构的树,第二个是initalFunctionOrJobProcessTree(tree,flag);这个是获得客户化的树形结

8、构,通过该方法可以在标准化得包结构下添加功能节点或后台事务定义节点。如果是生成功能(function)子树或实体(entity)子树,这就得获取这些元数据的集合,而它们对应的集合类:FunctionObjectCollection和EntityObjectCollection,获取这些集合的方法为:IMetaDataLoader loader = MetaDataLoaderFactory. getRemoteMetaDataLoader()FunctionObjectCollection funcol = loader.getFunctions()EntityObjectCollection

9、 entcol = loader.getEntityCollection();2.2. 后台事务发布后台事务定义完成以后,通过发布,我们就可以在后台事务定义报表看到我们定义的后台事务了,已发布的后台事务不允许进行删除。public void release(ProcessDef process) throws BOSException JobDef def=JobServiceUtil.toJobDef(ctx, process); Trigger trigger=JobServiceUtil.toTrigger(def, process); IJobService svc=JobServic

10、eFactory.getLocalInstance(ctx); svc.createJobDef(def, trigger, true);后台事务定义被发布以后,产生了一个新的对象JobDef,对用在数据库中的表是T_JOB_DEF。后台事务实例(Job)就是根据这个定义来产生的。由上面的代码可知,后台事务定义(JobDef)和触发器(Trigger)是由流程定义(ProcessDef)转换过来的,这是因为在以前的后台事务是按照工作流的方式进行处理的,每一个后台事务定义都会生成一个流程定义保存在数据库表(T_WFD_PROCESSDEF)的一个字段中,我们用代码实现后台事务定义就不必通过这样获

11、取流程定义(JobDef)。有上面代码可知,通过调用JobService的createJobDef(JobDef def, Trigger trigger, boolean enable);方法创建后台事务定义(实际上是通过反射调用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法),通过该方法将JobDef,Trigger对象保存到数据库中的T_JOB_DEF和T_TRIGGER表中。从而完成了后台事务定义的发布功能。2.3. 后台事务执行后台事务每执行一次就会产出一个后台事务实例(Job),持久化后

12、台事务会将后台事务实例信息保存在T_JOB_INST表中。 我们可以通过两种方式来触发后台事务执行,第一种是通过后台事务界面上的“启动”按钮直接触发产生一个后台事务实例,第二种是通过定义一个触发器(Trigger)来与该后台事务定义关联起来,用触发器来触发产生一个后台事务实例。下面我们分别来讨论通过这两种方法是怎样触发产生后台事务实例的。“启动”按钮触发后台事务。当我们点击“启动”按钮会触发该按钮的事件,会调用到该界面JobProcessDefineListUI的actionStart_actionPerformed方法,通过方法调用,我们可以知道它调用了JobServiceUIFacade的

13、createJobInstance(jobDefId, null, null),在这个类中,通过获取到了IjobService接口,调用了jobservice的createJobInstance方法;public String createJobInstance(String jobDefId, Object param, Timestamp scheduledTime) throws BOSException JobDef def=JobDefCache.getJobDef(ctx.getAIS(), jobDefId); if(def=null)throw new BOSException

14、(job def doesnt 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();从上面这个方法可以看出,首先由jobDefId获取到JobDef对象def,在设置这个产生的任务实例的计划

15、执行时间为当前时间的下一秒。然后由后台事务定义对象def的newInstance(ctx, param, scheduledTime)产生一个后台事务实例对象job,再由后台事务管理器JobManager(单子模式)调用add(job)方法把这个任务实例加载到等待队列或者就需队列中去;触发器触发后台事务。如果我们在后台事务定义的时候对其关联了相应的触发器,则当下一次触发时间到达时,触发器就会触发与之关联的后台事务定义(JobDef)产生一个后台事务实例(Job)。触发器触发后台事务的过程是怎样的呢?首先:服务器启动时会初始化触发器加载类(TriggerLoader),该类实现了IJobHand

16、ler, IcoreJobHandler两个接口,本身就是一个任务处理器,这个类的作用是:触发器加载者,定期从数据库加载新增的触发器。其load()方法会定期被调用执行/*加载触发器*/private int load()throws ExceptionStringBuffer sql=new StringBuffer();sql.append(select * from T_JOB_TRIGGER where fisvalid=Y AND fisAutoLoad=Y AND ); sql.append().append(TriggerIsolationLevel.sqlWhere(dc) .

17、append();/符合隔离边界sql.append( and fholderid is null);/无持有者/只加载已生效的触发器sql.append( and fvalidateTime=?);int types=new intTypes.TIMESTAMP;Timestamp now=new Timestamp(System.currentTimeMillis();Object values=new Objectnow;ArrayList items=SQL.executeQuery(dc, sql.toString(), types, values);Trigger tr;for(i

18、nt i=0; ie.getTime()job.setState(JobState.Missed);continue;/执行executed=execute(job);catch(Throwable t)log.error(execute job +job+ failed!,t);finally/释放任务RunningJobs.dispose(job);if(executed)/如果任务被成功执行try/调整任务定义属性adjustJobDef(job);/执行后处理handleReturns(job);catch(Throwable t)log.error(handler job +job+

19、s returns failed!,t);从这个方法可以看出,首先从就绪队列中取出一个任务,然后放入到放到运行时任务中,检查是否错过最后期限,这个值跟错过策略设置有关,如果设置为错过“立刻执行”,则这个值为null,如果为“忽略不计”则是在程序中设置的一个时间。如果设置为“忽略不计”,且执行最后期限已过,则设置为“已错过”的状态。接着就执行这个任务,在这个过程中要完成设置任务执行状态,触发一些事件,以及调用该任务的任务处理器对该任务进行处理。任务执行完以后,需要释放这个任务,并且调整任务定义的属性和执行一些后续处理。这样,就整个任务就执行完毕了。对持久化后台事务会产生一个后台事务实例,我们可以

20、通过“后台事务实例”按钮进行查看,执行失败的后台事务实例会产生日志信息,我们通过日志信息可以很快定位到执行后台事务失败的原因。3. 代码实现后台事务定义3.1. 非持久化后台事务在登陆EAS系统后,进入后台事务管理,在台事务定义的界面上我们是不能选择是定义持久化事务和非持久化事务的,通过在EAS系统中定义的后台事务默认都是持久化事务的。如果我们要定义非持久化后台事务,就必须通过代码来实现。通过对前一章的学习,我们已经熟悉后台事务执行的过程,我们可以在服务器端程序代码中新建一个后台事务定义(JobDef)的对象,再调用JobService的createJobDef(JobDef def)方法就可

21、以创建一个后台事务定义了。下面是我在后台事务客户端Faade的JobServiceUIFacade类中定义的一个方法。其中TestJob是一个任务处理类,实现了IJobHandler接口,可以通过在这个类中的execute()方法中实现自己的测试业务逻辑。public void createLogJobDef() throws BOSExceptionString packageName = null;String title= 后台事务日志记录测试;String description = 测试要求;String proxyUserID=null;String proxyOrgID=null

22、;boolean isPersistent=false;Wrapper wrapper=new InterfaceWrapper(new TestJob();JobDef def=new JobDef(ctx,packageName, title, description,proxyUserID, proxyOrgID, isPersistent, wrapper);JobServiceFactory.getLocalInstance(ctx).createJobDef(def);通过设置其isPersistent属性为false,这样创建的后台事务定义是一个非持久化后台事务定义。调用了Job

23、Service的createJobDef(def)方法,而这个方法又是通过反射调用了JobManager的createJobDef(JobDef def, Trigger trigger, boolean enable)方法,在这个方法会调用def.save(),即对我定义的后台事务定义JobDef进行数据库保存。这里面的trigger为空,即该后台事务没有关联触发器。我们需要执行这个方法,使之产生的后台事务定义可以在后台事务定义报表中显示出来,在这里,我是放在这个类的testJobDef方法中让它执行的。public String testJobDef(ProcessDef process)

24、 throws BOSException JobDef def=JobServiceUtil.toJobDef(ctx, process);createLogJobDef();/测试日志记录后台事务return JobServiceFactory.getLocalInstance(ctx).testJobDef(def, null);这个方法关联到后台事务定义界面上的“测试”按钮,所以只要点击那个按钮,就会执行我这个方法,从而产生一个后台事务定义对象并保存到数据库中,我们就可以在后台事务定义报表中看到我们定义的这条记录。由于这个后台事务没有关联触发器,所以要通过点击“启动”按钮触发后台事务,提

25、示后台事务启动成功后,点“后台事务实例”按钮,可以看到不会产生任务实例。这个后台事务完成的工作仍然是向D盘目录下的fileLog.txt文件中写时间,在测试之前可以先删除这个文件,测试后如果产生这个文件并记录下了当前时间则说明执行成功。3.2. 错过策略在后台事务定义的时候,可以进行设置后台事务的错过策略,有两种情况,一种是“立即执行”,另一种是“忽略不计”,其中这两种是怎样工作的呢?在后台事务定义的时候,后台事务错过策略信息是保存在T_JOB_DEF的fmissedtimeout字段中,设置后台事务的错过策略为立即执行,这个字段保存的值为-1,设置为忽略不计,这个字段保存的值为0;在代码中是

26、如何设置其错过的策略呢?通过下面这段代码你就可以明白系统是怎样进行处理的。if(scheduledTime=null)scheduledTime=new Timestamp(System.currentTimeMillis();if(expiredTime=null)int timeout=this.missedTimeout;int t=Configuration.persistantJobCheckInterval()*10;if(timeout=0)/默认错过超时, 取任务加载间隔的10倍, 且该值不得小于180秒timeout=t;if(timeout0)if(timeoute.get

27、Time()job.setState(JobState.Missed);continue;通常在多个线程同时运行的情况下,我们运行的后台事务实例不会因为阻塞而产生“已错过的情况”,如果我们需要对这种情况进行测试,这就需要我们在程序代码中进行控制,我们可以在后台任务管理器JobManager中的if(job.getScheduledTime().after(new Date()语句上设置一个断点,阻塞其运行,待产生的后台事务实例的最后期限已经过去,再去掉断点,让它们继续运行,这样我们就可以看到我们产生的后台实例出现“已错过”的情况。同样,我们可以进行错过策略为“立刻执行”的测试,不管我们的断点阻

28、塞线程运行多久,只要我们一让它运行,后台事务仍然会执行,如下图所示,开始执行时间比计划执行时间晚了3分多种,总而言之,选择“立刻执行”的后台事务实例,只要让它获得执行的机会就会直接执行。3.3. 人工干预(撤销,挂起,唤醒)对已接收,重新调度,已就绪,等待中的后台事务实例可进行撤销和挂起操作,对已挂起的后台事务实例可以进行唤醒操作,已错过的后台事务可以进行修复操作(其实质也就是重新调度),对运行中的后台事务不能够做任何操作,这些情况在实际运行中可能不是很好把握,因为从一个转台转换到另一个状态转换的时间间隔很少,所以要出现下面这些情况,最好还是通过设置断点,在不同的时机进行阻塞操作。3.4. 调

29、度计划时间后台事务触发器的调度计划时间表达式是保存在数据库表T_JOB_TRIGGER的fschedulePlan字段中,String schedulePlan=schedule.getType().toString()+:+schedule.getDefine();由这条语句可知,其值由两部分组成。分别为调度类别和调度的定义,其中调度定义是Quartz的cron 表达式的格式,后台事务获取下一次触发时间就是根据这个表达式来得到的,首先根据这个表达式可以新建一个CronTrigger对象,通过这个对象及上一次的触发时间就可以得到下一次触发时间了:quartzTrigger.getFireTim

30、eAfter(lastTriggeredTime)。下面是对Quartz的cron 表达式的格式的说明:Quartz 用 cron 表达式存放执行计划。引用了 cron 表达式的 CronTrigger 在计划的时间里会与 job 关联上。Quartz 提供七个域。下表列出了 Quartz cron 表达式支持的七个域。名称是否必须允许值特殊字符秒是0-59, - * /分是0-59, - * /时是0-23, - * /日是1-31, - * ? / L W C月是1-12 或 JAN-DEC, - * /周是1-7 或 SUN-SAT, - * ? / L C #年否空 或 1970-20

31、99, - * /月份和星期的名称是不区分大小写的。FRI 和 fri 是一样的。域之间有空格分隔,这和 UNIX cron 一样。无可争辩的,我们能写的最简单的表达式看起来就是这个了:* * * ? * *,这个表达会每秒钟(每分种的、每小时的、每天的)激发一个部署的 job。Quartz cron 表达式支持用特殊字符来创建更为复杂的执行计划:*星号:使用星号(*) 指示着你想在这个域上包含所有合法的值。例如,在月份域上使用星号意味着每个月都会触发这个 trigger。表达式样例:0 * 17 * * ?意义:每天从下午5点到下午5:59中的每分钟激发一次 trigger。它停在下午 5:

32、59 是因为值 17 在小时域上,在下午 6 点时,小时变为 18 了,也就不再理会这个 trigger,直到下一天的下午5点,在你希望 trigger 在该域的所有有效值上被激发时使用 * 字符。? 问号:? 号只能用在日和周域上,但是不能在这两个域上同时使用。你可以认为 ? 字符是 我并不关心在该域上是什么值。 这不同于星号,星号是指示着该域上的每一个值。? 是说不为该域指定值。不能同时这两个域上指定值的理由是难以解释甚至是难以理解的。基本上,假定同时指定值的话,意义就会变得含混不清了:考虑一下,如果一个表达式在日域上有值11,同时在周域上指定了 WED。那么是要 trigger 仅在每个

33、月的11号,且正好又是星期三那天被激发?还是在每个星期三的11号被激发呢?要去除这种不明确性的办法就是不能同时在这两个域上指定值。只要记住,假如你为这两域的其中一个指定了值,那就必须在另一个字值上放一个 ?。表达式样例:0 10,44 14 ? 3 WEB意义:在三月中的每个星期三的下午 2:10 和 下午 2:44 被触发。, 逗号:逗号 (,) 是用来在给某个域上指定一个值列表的。例如,使用值 0,15,30,45 在秒域上意味着每15秒触发一个 trigger。表达式样例: 0 0,15,30,45 * * * ?意义:每刻钟触发一次 trigger。/ 斜杠:斜杠 (/) 是用于时间表

34、的递增的。我们刚刚用了逗号来表示每15分钟的递增,但是我们也能写成这样 0/15。表达式样例: 0/15 0/30 * * * ?意义:在整点和半点时每15秒触发 trigger。- 中划线:中划线 (-) 用于指定一个范围。例如,在小时域上的 3-8 意味着 3,4,5,6,7 和 8 点。 域的值不允许回卷,所以像 50-10 这样的值是不允许的。表达式样例: 0 45 3-8 ? * *意义:在上午的3点至上午的8点的45分时触发 trigger。L 字母: L 说明了某域上允许的最后一个值。它仅被日和周域支持。当用在日域上,表示的是在月域上指定的月份的最后一天。例如,当月域上指定了 J

35、AN 时,在日域上的 L 会促使 trigger 在1月31号被触发。假如月域上是 SEP,那么 L 会预示着在9月30号触发。换句话说,就是不管指定了哪个月,都是在相应月份的时最后一天触发 trigger。表达式样例: 0 0 8 L * ? 意义:在每个月最后一天的上午 8:00 触发 trigger。在月域上的 * 说明是 每个月。当 L 字母用于周域上,指示着周的最后一天,就是星期六 (或者数字7)。所以如果你需要在每个月的最后一个星期六下午的 11:59 触发 trigger,你可以用这样的表达式 0 59 23 ? * L。当使用于周域上,你可以用一个数字与 L 连起来表示月份的最

36、后一个星期 X。例如,表达式 0 0 12 ? * 2L 说的是在每个月的最后一个星期一触发 trigger。注意:不要让范围和列表值与 L 连用,虽然你能用星期数(1-7)与 L 连用,但是不允许你用一个范围值和列表值与 L 连用。这会产生不可预知的结果。W 字母:W 字符代表着平日 (Mon-Fri),并且仅能用于日域中。它用来指定离指定日的最近的一个平日。大部分的商业处理都是基于工作周的,所以 W 字符可能是非常重要的。例如,日域中的 15W 意味着 离该月15号的最近一个平日。 假如15号是星期六,那么 trigger 会在14号(星期四)触发,因为距15号最近的是星期一,这个例子中也会是17号(译者Unmi注:不会在17号触发的,如果是15W,可能会是在14号(15号是星期六)或者15号(15号是星期天)触发,也就是只能出现在邻近的一天,如果15号当天为平日直接就会当日执行)。W 只能用在指定的日域为单天,不能是范围或列表值。# 井号:# 字符仅能用于周域中。它用于指定月份中的第几周的哪一天。例如,如果你指定周域的值为 6#3,它意思是某月的第三个周五 (6=星期五,#3意味着月份中的第三周)。另一个例子 2#1 意思是某月的第一个星期一 (2=星期一,#1意味着月份中的第一周)。注意,假如你指定 #5,然而月份中没有第 5 周,那么该月不会触发。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!