服务是定义为一段独立的逻辑程序,当多个服务组合在一起时就可以完成不同类型的业务需求。Ofbiz服务能调用其他服务定义,将多个小的服务串联起来实现一个大的任务。服务通过服务定义文件来定义并指派给具体的服务引擎。每个服务引擎通过适当方式来调用服务定义。在Ofbiz服务引擎中,服务需要使用Map传入参数,结果同样从Map中返回。
常见的几种服务类型
1.Interface服务引擎
interface 服务引擎实现在服务定义时可以共享相同的参数。一个接口服务不能被单独调用,服务功能是在实现服务接口的其他服务定义中实现的。每个接口服务都需要用interface 引擎来定义。
2.ECA(Event Condition Action)
ECA类似一个触发器,当一个服务被调用时,会检查是否为这个事件定义ECAs ,在验证、检验、事件实际调用、输出参数校验、事务提交或者服务返回之前包含进来。每个条件都会被验证,如果全部返回为真,定义的动作将会被执行。一个动作就是一个服务,服务的参数必须是已经存在于服务的上下文中。
每个ECA可以定义的条件数或者动作数没有限制。(用ServiceEcaRule的静态方法来调用)
3.服务组
服务组是由多个服务组成的服务集合,使用组服务定义文件来定义一个组服务,当这个组服务被调用时,组中定义的所有服务都会被调用。(ServiceGroupEngine.java)
组的定义比较简单,包含一个拥有多个 service 元素的 group 元素。group 元素包含一个 name 属性和一个 mode 属性, 其中mode 用来定义服务怎么执行。service 元类似于 ECA 中的action元素,不同之处在于 result-to-context 属性的默认值。
4.路由服务
路由服务使用路由服务引擎定义。当一个路由服务被调用时,所有定义的 ECA 会在适当事件中被运行。路由服务通过利用ECA服务选项可以路由( 'route') 到其他服务。(RouteEngine.java)
5.HTTP服务
HTTP服务是调用定义在其他系统上远程服务的一种方法。远程定义和本地定义是一致的,但其引擎应该是http。location 应该是httpService 事件在远程系统上运行的完全URL,方法是远程系统上被调用运行的服务名。在远程系统上的服务必须将export属性设为true允许远程调用。
HTTP 服务本质就是同步的。(HttpEngine.java)
6.异步服务
异步服务就是job scheduler异步调用的工作/服务。它包含池化的线程和几个请求线程。thread-pool 标签用来配置每个线程怎么操作。
OFBiz中services调用机制
Services的调用是从请求开始的,ControlServlet 就是所有请求过程的核心。当收到一个请求时,servlet 首先会设置系统环境信息,这里会将LocalDispatcher与Delegator设置到HttpServletRequest变量中去。View控制器Controller通过调用LocalDispatcher(实现类GenericDispatcher)将请求转发到ServiceDispatcher上,通过ServiceDispatcher来调用合适的服务引擎GenericEngine。服务引擎调用相应的实体引擎(Delegator的实现类GenericDelegator)来完成业务逻辑。事务管理是在ServiceDispatcher中完成的(异步调用最终也是在Job线程中调用ServiceDispatcher的同步方法)。
View控制器controller中如下调用服务:
LocalDispatcher dispatcher = dctx.getDispatcher();
Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
服务是声明在service.xml文件中的,声明格式如下:
<service name="createSurveyReport4Receipt" engine="java"确location="org.sample.warehouse.shipment.ShipmentReceiptServices" invoke="createSurveyReport4Receipt">
<description>Create SurveyReport for ShipmentReceipt</description>
<attribute name="receiptId" type="String" mode="INOUT" optional="false"/>
<attribute name="receiptTypeId" type="String" mode="IN" optional="true"/>
<attribute name="statusId" type="String" mode="IN" optional="true"/>
<attribute name="inspectId" type="String" mode="OUT" optional="true"/>
</service>
LocalDispatcher是本地调度器,实现服务的同步异步调度和定时任务的调度。服务最终是通过ServiceDispather分配到适当的服务引擎,最终交由GenericEngine来完成。每个实体引擎代理都有一个具体的 ServiceDispatcher实例(一个ServiceDispatcher实例对应一个实体引擎代理)。每个LocalDispatcher 都是唯一命名并包含自己的一些服务定义。
在ServiceDispatcher中,通过调用ServiceEcaUtil.evalRules()方法来执行ECA检验,并通过调用TransactionUtil方法来完成事务处理。
在请求处理过程中,如果遇到service的event,那么EventHandler会使用LocalDispatcher执行service。
同步调用
通过dispatcher调用runSync方法(即调用GenericDispatcher的runSync方法,最后转为ServiceDispatcher去完成任务- 调用dispatcher->runSync()方法):
/**
* @see org.ofbiz.service.LocalDispatcher#runSync(java.lang.String, java.util.Map)
*/
public Map<String, Object> runSync(String serviceName, Map<String, ? extends Object> context)
throws ServiceValidationException, GenericServiceException {
ModelService service = ctx.getModelService(serviceName);
return dispatcher.runSync(this.name, service, context); // dispatcher是ServiceDispatcher实例。
}
其中,dispatcher实际是ServiceDispatcher对象。ServiceDispatcher的runSync方法有三百多行,比较复杂,但最终调用service的是GenericEngine。
GenericEngine engine = this.getGenericEngine(modelService.engineName);
……
Map<String, Object> invokeResult = engine.runSync(localName, modelService, context); //engine是服务的实现
GenericEngine是在其工厂类GenericEngineFactory获取的。从配置文件serviceengine.xml文件中获取相应的engine子类,如java的org.ofbiz.service.engine.StandardJavaEngine、bsh的是org.ofbiz.service.engine.BeanShellEngine。
不同的Engine实现的方式不一样。Java的StandardJavaEnignerunSync方法采用的是反射来执行相应的方法,如下:
Class<?> c = cl.loadClass(this.getLocation(modelService));
Method m = c.getMethod(modelService.invoke, DispatchContext.class, Map.class);
if (Modifier.isStatic(m.getModifiers())) {
result = m.invoke(null, dctx, context);
} else {
result = m.invoke(c.newInstance(), dctx, context);
}
一个标准Java服务实现类代码类似如下:
public class xxxxx {
public static Map doxxx(DispatchContext dctx, Map context) {
GenericDelegator delegator = (GenericDelegator)dctx.getDelegator();
//从context中取出参数
Object param = context.get(…);
//调用实体引擎代理执行数据库业务操作。
delegator…..();
//结果保存在result中.
Map results = ServiceUtil.returnSuccess();
….
return results;
}
}
这样在ServiceDispatcher中执行engine.runSync(localName, modelService, context);最终通过具体的服务引擎实现来调用具体的业务逻辑实现。
异步调用
实现异步的原理就是启动一个线程来执行相应的业务逻辑,原方法直接返回,从而实现异步。具体实现可以根据实际情况而定,比如将业务逻辑封装成一个任务,再将此任务放到一个任务链中。最终由线程池选择任务进行执行。具体来查看GenericAsyncEngine的runAsync方法就会发现Ofbiz是通过生成一个Job来实现的:
job = new GenericServiceJob(dctx, jobId, name, modelService.name, context, requester);
try {
dispatcher.getJobManager().runJob(job);
} catch (JobManagerException jse) {
throw new GenericServiceException("Cannot run job.", jse);
}
OFBiz中Job的运行机制
OFBiz执行后台任务的类在org.ofbiz.service.job中。JobPoller和JobInvoker是其中主要的两个类,一个负责查询可以执行的Job,另一个执行Job任务。
在Ofbiz中基本上服务都是以下方式来运行的。
public void schedule(String jobName, String poolName, String serviceName, Map<String, ? extends Object> context, long startTime, int frequency, int interval, int count, long endTime, int maxRetry) throws GenericServiceException {
Transaction suspendedTransaction = null;
try {
boolean beganTransaction = false;
suspendedTransaction = TransactionUtil.suspend();
try {
beganTransaction = TransactionUtil.begin();
try {
getJobManager().schedule(jobName, poolName, serviceName, context, startTime, frequency, interval, count, endTime, maxRetry);
if (Debug.verboseOn()) {
Debug.logVerbose("[LocalDispatcher.schedule] : Current time : " + (new Date()).getTime(), module);
Debug.logVerbose("[LocalDispatcher.schedule] : Runtime : " + startTime, module);
Debug.logVerbose("[LocalDispatcher.schedule] : Frequency : " + frequency, module);
Debug.logVerbose("[LocalDispatcher.schedule] : Interval : " + interval, module);
Debug.logVerbose("[LocalDispatcher.schedule] : Count : " + count, module);
Debug.logVerbose("[LocalDispatcher.schedule] : EndTime : " + endTime, module);
Debug.logVerbose("[LocalDispatcher.schedule] : MazRetry : " + maxRetry, module);
}
} catch (JobManagerException jme) {
throw new GenericServiceException(jme.getMessage(), jme);
}
} catch (Exception e) {
String errMsg = "General error while scheduling job";
Debug.logError(e, errMsg, module);
try {
TransactionUtil.rollback(beganTransaction, errMsg, e);
} catch (GenericTransactionException gte1) {
Debug.logError(gte1, "Unable to rollback transaction", module);
}
} finally {
try {
TransactionUtil.commit(beganTransaction);
} catch (GenericTransactionException gte2) {
Debug.logError(gte2, "Unable to commit scheduled job", module);
}
}
} catch (GenericTransactionException gte) {
Debug.logError(gte, "Error suspending transaction while scheduling job", module);
} finally {
if (suspendedTransaction != null) {
try {
TransactionUtil.resume(suspendedTransaction);
} catch (GenericTransactionException gte3) {
Debug.logError(gte3, "Error resuming suspended transaction after scheduling job", module);
}
}
}
}